From ac7c366608439a7d8e79630a469c3460e11c8355 Mon Sep 17 00:00:00 2001 From: Naoki Kambe Date: Fri, 17 Aug 2012 13:46:16 +0900 Subject: [PATCH 001/199] [2158] updated the specification file of Xfrout added a definition of per-zone counters: notifyoutv4, notifyoutv6, xfrrej, and xfrreqdone. --- src/bin/xfrout/xfrout.spec.pre.in | 59 +++++++++++++++++++++++++++++++ 1 file changed, 59 insertions(+) diff --git a/src/bin/xfrout/xfrout.spec.pre.in b/src/bin/xfrout/xfrout.spec.pre.in index 6b113b0bf2..c59dee854c 100644 --- a/src/bin/xfrout/xfrout.spec.pre.in +++ b/src/bin/xfrout/xfrout.spec.pre.in @@ -114,6 +114,65 @@ "item_default": "IN" } ] } + ], + "statistics": [ + { + "item_name": "zones", + "item_type": "named_set", + "item_optional": false, + "item_default": { + "_SERVER_" : { + "notifyoutv4" : 0, + "notifyoutv6" : 0, + "xfrrej" : 0, + "xfrreqdone" : 0 + } + }, + "item_title": "Zone names", + "item_description": "Zone names for Xfrout statistics", + "named_set_item_spec": { + "item_name": "zonename", + "item_type": "map", + "item_optional": false, + "item_default": {}, + "item_title": "Zone name", + "item_description": "Zone name for Xfrout statistics", + "map_item_spec": [ + { + "item_name": "notifyoutv4", + "item_type": "integer", + "item_optional": false, + "item_default": 0, + "item_title": "IPv4 notifies", + "item_description": "Number of IPv4 notifies per zone name sent out from Xfrout" + }, + { + "item_name": "notifyoutv6", + "item_type": "integer", + "item_optional": false, + "item_default": 0, + "item_title": "IPv6 notifies", + "item_description": "Number of IPv6 notifies per zone name sent out from Xfrout" + }, + { + "item_name": "xfrrej", + "item_type": "integer", + "item_optional": false, + "item_default": 0, + "item_title": "XFR rejected requests", + "item_description": "Number of XFR requests per zone name rejected by Xfrout" + }, + { + "item_name": "xfrreqdone", + "item_type": "integer", + "item_optional": false, + "item_default": 0, + "item_title": "Requested zone transfers", + "item_description": "Number of requested zone transfers completed per zone name" + } + ] + } + } ] } } From 9db438071c5319ed0a09cbb16da88de67a2101be Mon Sep 17 00:00:00 2001 From: Naoki Kambe Date: Fri, 17 Aug 2012 13:50:52 +0900 Subject: [PATCH 002/199] [2158] added a new message id for logging It would be logged when b10-xfrout receives the "getstats" command from the b10-stats daemon. --- src/bin/xfrout/xfrout_messages.mes | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/bin/xfrout/xfrout_messages.mes b/src/bin/xfrout/xfrout_messages.mes index 9f674a2cf8..bef6080ba1 100644 --- a/src/bin/xfrout/xfrout_messages.mes +++ b/src/bin/xfrout/xfrout_messages.mes @@ -107,6 +107,10 @@ received from the configuration manager. The xfrout daemon received a command on the command channel that NOTIFY packets should be sent for the given zone. +% XFROUT_RECEIVED_GETSTATS_COMMAND received command to get statistics data +The xfrout daemon received a command on the command channel that +statistics data should be sent to the stats daemon. + % XFROUT_PARSE_QUERY_ERROR error parsing query: %1 There was a parse error while reading an incoming query. The parse error is shown in the log message. A remote client sent a packet we From 9acbaae908b7aac9123deb5dfa08e199aff4ba8e Mon Sep 17 00:00:00 2001 From: Naoki Kambe Date: Fri, 17 Aug 2012 15:38:18 +0900 Subject: [PATCH 003/199] [2158] updated for counting xfr requests - added the new class XfroutCounter It is intended for producing incrementers of the counters based on the statistics spec and for outputting statistics data format to passed to the b10-stats daemon. - added counter handlers passed to the NotifyOut object and the XfroutSession object - added handling of the getstats command - added counting xfr requests in the XfroutSession class - updated the UnixSockServer class for taking over the counter handlers --- src/bin/xfrout/xfrout.py.in | 157 +++++++++++++++++++++++++++++++++--- 1 file changed, 148 insertions(+), 9 deletions(-) diff --git a/src/bin/xfrout/xfrout.py.in b/src/bin/xfrout/xfrout.py.in index 6576432d78..3d1801e39a 100755 --- a/src/bin/xfrout/xfrout.py.in +++ b/src/bin/xfrout/xfrout.py.in @@ -153,7 +153,8 @@ def get_soa_serial(soa_rdata): class XfroutSession(): def __init__(self, sock_fd, request_data, server, tsig_key_ring, remote, - default_acl, zone_config, client_class=DataSourceClient): + default_acl, zone_config, client_class=DataSourceClient, + counter_xfrrej=None, counter_xfrreqdone=None): self._sock_fd = sock_fd self._request_data = request_data self._server = server @@ -168,6 +169,14 @@ class XfroutSession(): self.ClientClass = client_class # parameterize this for testing self._soa = None # will be set in _xfrout_setup or in tests self._jnl_reader = None # will be set to a reader for IXFR + # Set counter handlers for counting Xfr requests. An argument + # is required for zone name. + self._counter_xfrrej = lambda x: None + if hasattr(counter_xfrrej, '__call__'): + self._counter_xfrrej = counter_xfrrej + self._counter_xfrreqdone = lambda x: None + if hasattr(counter_xfrreqdone, '__call__'): + self._counter_xfrreqdone = counter_xfrreqdone self._handle() def create_tsig_ctx(self, tsig_record, tsig_key_ring): @@ -270,6 +279,8 @@ class XfroutSession(): format_zone_str(zone_name, zone_class)) return None, None elif acl_result == REJECT: + # count rejected Xfr request by each zone name + self._counter_xfrrej(zone_name.to_text()) logger.debug(DBG_XFROUT_TRACE, XFROUT_QUERY_REJECTED, self._request_type, format_addrinfo(self._remote), format_zone_str(zone_name, zone_class)) @@ -525,6 +536,8 @@ class XfroutSession(): except Exception as err: logger.error(XFROUT_XFR_TRANSFER_ERROR, self._request_typestr, format_addrinfo(self._remote), zone_str, err) + # count done Xfr requests by each zone name + self._counter_xfrreqdone(zone_name.to_text()) logger.info(XFROUT_XFR_TRANSFER_DONE, self._request_typestr, format_addrinfo(self._remote), zone_str) @@ -634,7 +647,7 @@ class UnixSockServer(socketserver_mixin.NoPollMixIn, '''The unix domain socket server which accept xfr query sent from auth server.''' def __init__(self, sock_file, handle_class, shutdown_event, config_data, - cc): + cc, **counters): self._remove_unused_sock_file(sock_file) self._sock_file = sock_file socketserver_mixin.NoPollMixIn.__init__(self) @@ -644,6 +657,8 @@ class UnixSockServer(socketserver_mixin.NoPollMixIn, self._common_init() self._cc = cc self.update_config_data(config_data) + # handlers for statistics use + self._counters = counters def _common_init(self): '''Initialization shared with the mock server class used for tests''' @@ -798,7 +813,8 @@ class UnixSockServer(socketserver_mixin.NoPollMixIn, self._lock.release() self.RequestHandlerClass(sock_fd, request_data, self, isc.server_common.tsig_keyring.get_keyring(), - self._guess_remote(sock_fd), acl, zone_config) + self._guess_remote(sock_fd), acl, zone_config, + **self._counters) def _remove_unused_sock_file(self, sock_file): '''Try to remove the socket file. If the file is being used @@ -926,6 +942,109 @@ class UnixSockServer(socketserver_mixin.NoPollMixIn, self._transfers_counter -= 1 self._lock.release() +class XfroutCounter: + """A class for handling all statistics counters of Xfrout. In + this class, the structure of per-zone counters is assumed to be + like this: + zones/example.com./notifyoutv4 + zones/example.com./notifyoutv6 + zones/example.com./xfrrej + zones/example.com./xfrreqdone + """ + # '_SERVER_' is a special zone name representing an entire + # count. It doesn't mean a specific zone, but it means an + # entire count in the server. + entire_server = '_SERVER_' + # zone names are contained under this dirname in the spec file. + perzone_prefix = 'zones' + def __init__(self, statistics_spec): + self._statistics_spec = statistics_spec + # holding statistics data for Xfrout module + self._statistics_data = {} + self._lock = threading.RLock() + self._create_perzone_incrementers() + + def get_statistics(self): + """Calculates an entire server counts, and returns statistics + data format to send out the stats module including each + counter. If there is no counts, then it returns an empty + dictionary. Locks the thread because it is considered to be + invoked by a multi-threading caller.""" + # If self._statistics_data contains nothing of zone name, it + # returns an empty dict. + if len(self._statistics_data) == 0: return {} + zones = {} + with self._lock: + zones = self._statistics_data[self.perzone_prefix].copy() + # Start calculation for '_SERVER_' counts + attrs = self._get_default_statistics_data()[self.perzone_prefix][self.entire_server] + statistics_data = {self.perzone_prefix: {}} + for attr in attrs: + sum_ = 0 + for name in zones: + if name == self.entire_server: continue + if attr in zones[name]: + if name not in statistics_data[self.perzone_prefix]: + statistics_data[self.perzone_prefix][name] = {} + statistics_data[self.perzone_prefix][name].update( + {attr: zones[name][attr]} + ) + sum_ += zones[name][attr] + if sum_ > 0: + if self.entire_server not in statistics_data[self.perzone_prefix]: + statistics_data[self.perzone_prefix][self.entire_server] = {} + statistics_data[self.perzone_prefix][self.entire_server].update({attr: sum_}) + return statistics_data + + def _get_default_statistics_data(self): + """Returns default statistics data from the spec file""" + statistics_data = {} + for id_ in isc.config.spec_name_list(self._statistics_spec): + spec = isc.config.find_spec_part(self._statistics_spec, id_) + statistics_data.update({id_: spec['item_default']}) + return statistics_data + + def _create_perzone_incrementers(self): + """Creates increment method of each per-zone counter based on + the spec file. Incrementer can be accessed by name + "inc_${item_name}".Incrementers are passed to the + XfroutSession and NotifyOut class as counter handlers.""" + # add a new element under the named_set item for the zone + zones_spec = isc.config.find_spec_part( + self._statistics_spec, self.perzone_prefix) + item_list = isc.config.spec_name_list(\ + zones_spec['named_set_item_spec']['map_item_spec']) + # can be accessed by the name 'inc_xxx' + for item in item_list: + def __perzone_incrementer(zone_name, counter_name=item, step=1): + """A per-zone incrementer for counter_name. Locks the thread + because it is considered to be invoked by a multi-threading + caller.""" + with self._lock: + self._add_perzone_counter(zone_name) + self._statistics_data[self.perzone_prefix][zone_name][counter_name] += step + #def __perzone_incrementer(zone_name, counter_name=item): + # self._perzone_incrementer(zone_name, counter_name) + setattr(self, 'inc_%s' % item, __perzone_incrementer) + + + def _add_perzone_counter(self, zone): + """Adds named_set-type counter for each zone name""" + try: + self._statistics_data[self.perzone_prefix][zone] + except KeyError: + # add a new element under the named_set item for the zone + map_spec = isc.config.find_spec_part( + self._statistics_spec, '%s/%s' % \ + (self.perzone_prefix, zone))['map_item_spec'] + id_list = isc.config.spec_name_list(map_spec) + for id_ in id_list: + spec = isc.config.find_spec_part(map_spec, id_) + isc.cc.data.set(self._statistics_data, + '%s/%s/%s' % \ + (self.perzone_prefix, zone, id_), + spec['item_default']) + class XfroutServer: def __init__(self): self._unix_socket_server = None @@ -933,6 +1052,8 @@ class XfroutServer: self._shutdown_event = threading.Event() self._cc = isc.config.ModuleCCSession(SPECFILE_LOCATION, self.config_handler, self.command_handler) self._config_data = self._cc.get_full_config() + self._counter = XfroutCounter( + self._cc.get_module_spec().get_statistics_spec()) self._cc.start() self._cc.add_remote_config(AUTH_SPECFILE_LOCATION) isc.server_common.tsig_keyring.init_keyring(self._cc) @@ -941,17 +1062,25 @@ class XfroutServer: def _start_xfr_query_listener(self): '''Start a new thread to accept xfr query. ''' - self._unix_socket_server = UnixSockServer(self._listen_sock_file, - XfroutSession, - self._shutdown_event, - self._config_data, - self._cc) + self._unix_socket_server = UnixSockServer( + self._listen_sock_file, + XfroutSession, + self._shutdown_event, + self._config_data, + self._cc, + counter_xfrrej=self._counter.inc_xfrrej, + counter_xfrreqdone=self._counter.inc_xfrreqdone + ) listener = threading.Thread(target=self._unix_socket_server.serve_forever) listener.start() def _start_notifier(self): datasrc = self._unix_socket_server.get_db_file() - self._notifier = notify_out.NotifyOut(datasrc) + self._notifier = notify_out.NotifyOut( + datasrc, + counter_notifyoutv4=self._counter.inc_notifyoutv4, + counter_notifyoutv6=self._counter.inc_notifyoutv6 + ) if 'also_notify' in self._config_data: for slave in self._config_data['also_notify']: self._notifier.add_slave(slave['address'], slave['port']) @@ -1027,6 +1156,16 @@ class XfroutServer: else: answer = create_answer(1, "Bad command parameter:" + str(args)) + # return statistics data to the stats daemon + elif cmd == "getstats": + # The log level is here set to debug in order to avoid + # that a log becomes too verbose. Because the b10-stats + # daemon is periodically asking to the b10-xfrout daemon. + logger.debug(DBG_XFROUT_TRACE, \ + XFROUT_RECEIVED_GETSTATS_COMMAND) + answer = isc.config.ccsession.create_answer( + 0, self._counter.get_statistics()) + else: answer = create_answer(1, "Unknown command:" + str(cmd)) From b6bba799f9422b6bc0003bc2e34d9bdc1fa9da67 Mon Sep 17 00:00:00 2001 From: Naoki Kambe Date: Fri, 17 Aug 2012 14:10:54 +0900 Subject: [PATCH 004/199] [2158] updated unittest of Xfrout - updated the mock classes for testing counters - added tests checking each counter exactly incrementing - added tests for the XfroutCounter class - added a test for handling the getstats command --- src/bin/xfrout/tests/xfrout_test.py.in | 100 ++++++++++++++++++++++++- 1 file changed, 99 insertions(+), 1 deletion(-) diff --git a/src/bin/xfrout/tests/xfrout_test.py.in b/src/bin/xfrout/tests/xfrout_test.py.in index e4fc873e46..39ac2df2b8 100644 --- a/src/bin/xfrout/tests/xfrout_test.py.in +++ b/src/bin/xfrout/tests/xfrout_test.py.in @@ -277,13 +277,23 @@ class TestXfroutSessionBase(unittest.TestCase): # When not testing ACLs, simply accept isc.acl.dns.REQUEST_LOADER.load( [{"action": "ACCEPT"}]), - {}) + {}, + counter_xfrrej=self._counter_xfrrej, + counter_xfrreqdone=self._counter_xfrreqdone) self.set_request_type(RRType.AXFR()) # test AXFR by default self.mdata = self.create_request_data() self.soa_rrset = create_soa(SOA_CURRENT_VERSION) # some test replaces a module-wide function. We should ensure the # original is used elsewhere. self.orig_get_rrset_len = xfrout.get_rrset_len + self._zone_name_xfrrej = None + self._zone_name_xfrreqdone = None + + def _counter_xfrrej(self, zone_name): + self._zone_name_xfrrej = zone_name + + def _counter_xfrreqdone(self, zone_name): + self._zone_name_xfrreqdone = zone_name def tearDown(self): xfrout.get_rrset_len = self.orig_get_rrset_len @@ -458,7 +468,9 @@ class TestXfroutSession(TestXfroutSessionBase): # ACL checks only with the default ACL def acl_setter(acl): self.xfrsess._acl = acl + self.assertIsNone(self._zone_name_xfrrej) self.check_transfer_acl(acl_setter) + self.assertEqual(self._zone_name_xfrrej, TEST_ZONE_NAME_STR) def test_transfer_zoneacl(self): # ACL check with a per zone ACL + default ACL. The per zone ACL @@ -469,7 +481,9 @@ class TestXfroutSession(TestXfroutSessionBase): self.xfrsess._zone_config[zone_key]['transfer_acl'] = acl self.xfrsess._acl = isc.acl.dns.REQUEST_LOADER.load([ {"from": "127.0.0.1", "action": "DROP"}]) + self.assertIsNone(self._zone_name_xfrrej) self.check_transfer_acl(acl_setter) + self.assertEqual(self._zone_name_xfrrej, TEST_ZONE_NAME_STR) def test_transfer_zoneacl_nomatch(self): # similar to the previous one, but the per zone doesn't match the @@ -481,7 +495,9 @@ class TestXfroutSession(TestXfroutSessionBase): isc.acl.dns.REQUEST_LOADER.load([ {"from": "127.0.0.1", "action": "DROP"}]) self.xfrsess._acl = acl + self.assertIsNone(self._zone_name_xfrrej) self.check_transfer_acl(acl_setter) + self.assertEqual(self._zone_name_xfrrej, TEST_ZONE_NAME_STR) def test_get_transfer_acl(self): # set the default ACL. If there's no specific zone ACL, this one @@ -831,9 +847,11 @@ class TestXfroutSession(TestXfroutSessionBase): def myreply(msg, sock): self.sock.send(b"success") + self.assertIsNone(self._zone_name_xfrreqdone) self.xfrsess._reply_xfrout_query = myreply self.xfrsess.dns_xfrout_start(self.sock, self.mdata) self.assertEqual(self.sock.readsent(), b"success") + self.assertEqual(self._zone_name_xfrreqdone, TEST_ZONE_NAME_STR) def test_reply_xfrout_query_axfr(self): self.xfrsess._soa = self.soa_rrset @@ -1153,6 +1171,7 @@ class MyUnixSockServer(UnixSockServer): self._common_init() self._cc = MyCCSession() self.update_config_data(self._cc.get_full_config()) + self._counters = {} class TestUnixSockServer(unittest.TestCase): def setUp(self): @@ -1504,6 +1523,80 @@ class MyXfroutServer(XfroutServer): self._unix_socket_server = None # Disable the wait for threads self._wait_for_threads = lambda : None + self._cc.get_module_spec = lambda:\ + isc.config.module_spec_from_file(xfrout.SPECFILE_LOCATION) + # setup an XfroutCount object + self._counter = XfroutCounter( + self._cc.get_module_spec().get_statistics_spec()) + +class TestXfroutCounter(unittest.TestCase): + def setUp(self): + statistics_spec = \ + isc.config.module_spec_from_file(\ + xfrout.SPECFILE_LOCATION).get_statistics_spec() + self.xfrout_counter = XfroutCounter(statistics_spec) + self._counters = isc.config.spec_name_list(\ + isc.config.find_spec_part(\ + statistics_spec, XfroutCounter.perzone_prefix)\ + ['named_set_item_spec']['map_item_spec']) + self._started = threading.Event() + self._number = 3 # number of the threads + self._cycle = 10000 # number of counting per thread + + def test_get_default_statistics_data(self): + self.assertEqual(self.xfrout_counter._get_default_statistics_data(), + {XfroutCounter.perzone_prefix: { + XfroutCounter.entire_server: \ + dict([(cnt, 0) for cnt in self._counters]) + }}) + + def setup_incrementer(self, incrementer): + self._started.wait() + for i in range(self._cycle): incrementer(TEST_ZONE_NAME_STR) + + def start_incrementer(self, incrementer): + threads = [] + for i in range(self._number): + threads.append(threading.Thread(\ + target=self.setup_incrementer,\ + args=(incrementer,)\ + )) + for th in threads: th.start() + self._started.set() + for th in threads: th.join() + + def get_count(self, zone_name, counter_name): + return isc.cc.data.find(\ + self.xfrout_counter.get_statistics(),\ + '%s/%s/%s' % (XfroutCounter.perzone_prefix,\ + zone_name, counter_name)) + + def test_incrementers(self): + result = { XfroutCounter.entire_server: {}, + TEST_ZONE_NAME_STR: {} } + for counter_name in self._counters: + incrementer = getattr(self.xfrout_counter, 'inc_%s' % counter_name) + self.start_incrementer(incrementer) + self.assertEqual(self.get_count(\ + TEST_ZONE_NAME_STR, counter_name), \ + self._number * self._cycle) + self.assertEqual(self.get_count(\ + XfroutCounter.entire_server, counter_name), \ + self._number * self._cycle) + result[XfroutCounter.entire_server][counter_name] = \ + result[TEST_ZONE_NAME_STR][counter_name] = \ + self._number * self._cycle + self.assertEqual( + self.xfrout_counter.get_statistics(), + {XfroutCounter.perzone_prefix: result}) + + def test_add_perzone_counter(self): + for counter_name in self._counters: + self.assertRaises(isc.cc.data.DataNotFoundError,\ + self.get_count, TEST_ZONE_NAME_STR, counter_name) + self.xfrout_counter._add_perzone_counter(TEST_ZONE_NAME_STR) + for counter_name in self._counters: + self.assertEqual(self.get_count(TEST_ZONE_NAME_STR, counter_name), 0) class TestXfroutServer(unittest.TestCase): def setUp(self): @@ -1514,6 +1607,11 @@ class TestXfroutServer(unittest.TestCase): self.assertTrue(self.xfrout_server._notifier.shutdown_called) self.assertTrue(self.xfrout_server._cc.stopped) + def test_getstats(self): + self.assertEqual( + self.xfrout_server.command_handler('getstats', None), + isc.config.ccsession.create_answer(0, {})) + if __name__== "__main__": isc.log.resetUnitTestRootLogger() unittest.main() From f483892f37e50ed1a0afe0cc4e1e4f5a1f58a035 Mon Sep 17 00:00:00 2001 From: Naoki Kambe Date: Fri, 17 Aug 2012 15:40:07 +0900 Subject: [PATCH 005/199] [2158] updated the NotifyOut class for counting notifications - added setting up the counter handlers for IPv4 and IPv6 - added counting notifications in the NotifyOut class - fixed a trivial typo --- src/lib/python/isc/notify/notify_out.py | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/src/lib/python/isc/notify/notify_out.py b/src/lib/python/isc/notify/notify_out.py index 34db14c5c3..8d632d06ef 100644 --- a/src/lib/python/isc/notify/notify_out.py +++ b/src/lib/python/isc/notify/notify_out.py @@ -125,9 +125,10 @@ class ZoneNotifyInfo: class NotifyOut: '''This class is used to handle notify logic for all zones(sending notify message to its slaves). notify service can be started by - calling dispatcher(), and it can be stoped by calling shutdown() + calling dispatcher(), and it can be stopped by calling shutdown() in another thread. ''' - def __init__(self, datasrc_file, verbose=True): + def __init__(self, datasrc_file, counter_handler=None, verbose=True, + counter_notifyoutv4=None, counter_notifyoutv6=None): self._notify_infos = {} # key is (zone_name, zone_class) self._waiting_zones = [] self._notifying_zones = [] @@ -142,6 +143,14 @@ class NotifyOut: # Use nonblock event to eliminate busy loop # If there are no notifying zones, clear the event bit and wait. self._nonblock_event = threading.Event() + # Set counter handlers for counting notifies. An argument is + # required for zone name. + self._counter_notifyoutv4 = lambda x: None + if hasattr(counter_notifyoutv4, '__call__'): + self._counter_notifyoutv4 = counter_notifyoutv4 + self._counter_notifyoutv6 = lambda x: None + if hasattr(counter_notifyoutv6, '__call__'): + self._counter_notifyoutv6 = counter_notifyoutv6 def _init_notify_out(self, datasrc_file): '''Get all the zones name and its notify target's address. @@ -478,6 +487,11 @@ class NotifyOut: try: sock = zone_notify_info.create_socket(addrinfo[0]) sock.sendto(render.get_data(), 0, addrinfo) + # count notifying by IPv4 or IPv6 for statistics + if zone_notify_info.get_socket().family == socket.AF_INET: + self._counter_notifyoutv4(zone_notify_info.zone_name) + elif zone_notify_info.get_socket().family == socket.AF_INET6: + self._counter_notifyoutv6(zone_notify_info.zone_name) logger.info(NOTIFY_OUT_SENDING_NOTIFY, addrinfo[0], addrinfo[1]) except (socket.error, addr.InvalidAddress) as err: From d3bc2109f07fac785c5a75041f4e88696dbc6a33 Mon Sep 17 00:00:00 2001 From: Naoki Kambe Date: Fri, 17 Aug 2012 14:20:08 +0900 Subject: [PATCH 006/199] [2158] updated unittest for checking the notification counters - updated the mock classes for testing counters - added tests of each counter with a specified zone name --- .../isc/notify/tests/notify_out_test.py | 21 ++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/src/lib/python/isc/notify/tests/notify_out_test.py b/src/lib/python/isc/notify/tests/notify_out_test.py index 1b3a4a128d..8343d0bf53 100644 --- a/src/lib/python/isc/notify/tests/notify_out_test.py +++ b/src/lib/python/isc/notify/tests/notify_out_test.py @@ -61,6 +61,7 @@ class MockZoneNotifyInfo(notify_out.ZoneNotifyInfo): self.sock_family = self._sock.family self._sock.close() self._sock = MockSocket() + self._sock.family = self.sock_family return self._sock class TestZoneNotifyInfo(unittest.TestCase): @@ -95,7 +96,13 @@ class TestZoneNotifyInfo(unittest.TestCase): class TestNotifyOut(unittest.TestCase): def setUp(self): self._db_file = TESTDATA_SRCDIR + '/test.sqlite3' - self._notify = notify_out.NotifyOut(self._db_file) + self._notifiedv4_zone_name = None + def _dummy_counter_notifyoutv4(z): self._notifiedv4_zone_name = z + self._notifiedv6_zone_name = None + def _dummy_counter_notifyoutv6(z): self._notifiedv6_zone_name = z + self._notify = notify_out.NotifyOut(self._db_file, + counter_notifyoutv4=_dummy_counter_notifyoutv4, + counter_notifyoutv6=_dummy_counter_notifyoutv6) self._notify._notify_infos[('example.com.', 'IN')] = MockZoneNotifyInfo('example.com.', 'IN') self._notify._notify_infos[('example.com.', 'CH')] = MockZoneNotifyInfo('example.com.', 'CH') self._notify._notify_infos[('example.net.', 'IN')] = MockZoneNotifyInfo('example.net.', 'IN') @@ -262,17 +269,25 @@ class TestNotifyOut(unittest.TestCase): def test_send_notify_message_udp_ipv4(self): example_com_info = self._notify._notify_infos[('example.net.', 'IN')] example_com_info.prepare_notify_out() + self.assertIsNone(self._notifiedv4_zone_name) + self.assertIsNone(self._notifiedv6_zone_name) ret = self._notify._send_notify_message_udp(example_com_info, ('192.0.2.1', 53)) self.assertTrue(ret) self.assertEqual(socket.AF_INET, example_com_info.sock_family) + self.assertEqual(self._notifiedv4_zone_name, 'example.net.') + self.assertIsNone(self._notifiedv6_zone_name) def test_send_notify_message_udp_ipv6(self): example_com_info = self._notify._notify_infos[('example.net.', 'IN')] + self.assertIsNone(self._notifiedv4_zone_name) + self.assertIsNone(self._notifiedv6_zone_name) ret = self._notify._send_notify_message_udp(example_com_info, ('2001:db8::53', 53)) self.assertTrue(ret) self.assertEqual(socket.AF_INET6, example_com_info.sock_family) + self.assertIsNone(self._notifiedv4_zone_name) + self.assertEqual(self._notifiedv6_zone_name, 'example.net.') def test_send_notify_message_with_bogus_address(self): example_com_info = self._notify._notify_infos[('example.net.', 'IN')] @@ -281,9 +296,13 @@ class TestNotifyOut(unittest.TestCase): # happen, but right now it's not actually the case. Even if the # data source does its job, it's prudent to confirm the behavior for # an unexpected case. + self.assertIsNone(self._notifiedv4_zone_name) + self.assertIsNone(self._notifiedv6_zone_name) ret = self._notify._send_notify_message_udp(example_com_info, ('invalid', 53)) self.assertFalse(ret) + self.assertIsNone(self._notifiedv4_zone_name) + self.assertIsNone(self._notifiedv6_zone_name) def test_zone_notify_handler(self): old_send_msg = self._notify._send_notify_message_udp From 9007b87d062335814cbef0b579003a61d6442e57 Mon Sep 17 00:00:00 2001 From: Naoki Kambe Date: Fri, 17 Aug 2012 14:33:56 +0900 Subject: [PATCH 007/199] [2158] updated the lettuce test suite - introduced the b10-stats daemon running in the lettuce test suite "Xfrin incoming notify handling" - renamed retransfer_master.conf to retransfer_master.conf.orig because of the config change in the lettuce suite --- tests/lettuce/configurations/xfrin/.gitignore | 1 + .../{retransfer_master.conf => retransfer_master.conf.orig} | 1 + tests/lettuce/features/terrain/terrain.py | 2 ++ 3 files changed, 4 insertions(+) create mode 100644 tests/lettuce/configurations/xfrin/.gitignore rename tests/lettuce/configurations/xfrin/{retransfer_master.conf => retransfer_master.conf.orig} (93%) diff --git a/tests/lettuce/configurations/xfrin/.gitignore b/tests/lettuce/configurations/xfrin/.gitignore new file mode 100644 index 0000000000..4de8c4b137 --- /dev/null +++ b/tests/lettuce/configurations/xfrin/.gitignore @@ -0,0 +1 @@ +/retransfer_master.conf diff --git a/tests/lettuce/configurations/xfrin/retransfer_master.conf b/tests/lettuce/configurations/xfrin/retransfer_master.conf.orig similarity index 93% rename from tests/lettuce/configurations/xfrin/retransfer_master.conf rename to tests/lettuce/configurations/xfrin/retransfer_master.conf.orig index dd29ac893e..b0e3ac0b88 100644 --- a/tests/lettuce/configurations/xfrin/retransfer_master.conf +++ b/tests/lettuce/configurations/xfrin/retransfer_master.conf.orig @@ -38,6 +38,7 @@ "b10-auth": { "kind": "needed", "special": "auth" }, "b10-xfrout": { "address": "Xfrout", "kind": "dispensable" }, "b10-zonemgr": { "address": "Zonemgr", "kind": "dispensable" }, + "b10-stats": { "address": "Stats", "kind": "dispensable" }, "b10-cmdctl": { "special": "cmdctl", "kind": "needed" } } } diff --git a/tests/lettuce/features/terrain/terrain.py b/tests/lettuce/features/terrain/terrain.py index 876e7cfd48..8720e2d994 100644 --- a/tests/lettuce/features/terrain/terrain.py +++ b/tests/lettuce/features/terrain/terrain.py @@ -57,6 +57,8 @@ copylist = [ "configurations/ddns/ddns.config"], ["configurations/ddns/noddns.config.orig", "configurations/ddns/noddns.config"], + ["configurations/xfrin/retransfer_master.conf.orig", + "configurations/xfrin/retransfer_master.conf"], ["data/inmem-xfrin.sqlite3.orig", "data/inmem-xfrin.sqlite3"], ["data/xfrin-notify.sqlite3.orig", From b18cb62ebeea655fcc277a2478a9ee560a33408b Mon Sep 17 00:00:00 2001 From: Naoki Kambe Date: Fri, 17 Aug 2012 14:42:29 +0900 Subject: [PATCH 008/199] [2158] added the lettuce step methods for statistics - added the two step methods and a helper method That is intended for querying statistics counters via bindctl and for checking whether the returned values of the counters are exactly expected ones. --- .../features/terrain/bind10_control.py | 61 +++++++++++++++++++ 1 file changed, 61 insertions(+) diff --git a/tests/lettuce/features/terrain/bind10_control.py b/tests/lettuce/features/terrain/bind10_control.py index b661657bce..b1c8aced7a 100644 --- a/tests/lettuce/features/terrain/bind10_control.py +++ b/tests/lettuce/features/terrain/bind10_control.py @@ -362,3 +362,64 @@ def configure_ddns_off(step): config commit \"\"\" """) + +@step('query statistics(?: (\S+))? of bind10 module (\S+)(?: with cmdctl port (\d+))?') +def query_statistics(step, statistics, name, cmdctl_port): + """ + query statistics data via bindctl. + Parameters: + statistics ('statistics ', optional) : The queried statistics name. + name ('module '): The name of the module (case sensitive!) + cmdctl_port ('with cmdctl port ', optional): cmdctl port to send + the command to. + """ + port_str = ' with cmdctl port %s' % cmdctl_port \ + if cmdctl_port else '' + step.given('send bind10%s the command Stats show owner=%s%s'\ + % (port_str, name,\ + ' name=%s' % statistics if statistics else '')) + +def find_value(dictionary, key): + """A helper method. Recursively find a value corresponding to the + key of the dictionary and returns it. Returns None if the + dictionary is not dict type.""" + if type(dictionary) is not dict: + return + if key in dictionary: + return dictionary[key] + else: + for v in dictionary.values(): + return find_value(v, key) + +@step('The counter (\S+)(?: for the zone (\S+))? should be' + \ + '(?:( greater than| less than))? (\d+)') +def check_statistics(step, counter, zone, gtlt, number): + """ + check the output of bindctl for statistics of specified counter + and zone. + Parameters: + counter ('counter '): The counter name of statistics. + zone ('zone ', optional): The zone name. + gtlt (' greater than'|' less than', optional): greater than + or less than . + number ('): The expect counter number. is assumed + to be an unsigned integer. + """ + output = parse_bindctl_output_as_data_structure() + found = None + zone_str = "" + if zone: + found = find_value(find_value(output, zone), counter) + zone_str = " for zone %s" % zone + else: + found = find_value(output, counter) + assert found is not None, \ + 'Not found statistics counter %s%s' % (counter, zone_str) + msg = "Got %s, expected%s %s as counter %s%s" % \ + (found, gtlt, number, counter, zone_str) + if gtlt and 'greater' in gtlt: + assert int(found) > int(number), msg + elif gtlt and 'less' in gtlt: + assert int(found) < int(number), msg + else: + assert int(found) == int(number), msg From 314157f8af6d820c7beee0eb720951239752e008 Mon Sep 17 00:00:00 2001 From: Naoki Kambe Date: Fri, 17 Aug 2012 15:10:31 +0900 Subject: [PATCH 009/199] [2158] updated the xfrin_notify_handling feature - querying each statistics counter of the b10-xfrout daemon - checking correct values of the counters and no errors after querying - added waiting for some events in the debug log - added a similar scenario that xfrin requests are rejected in order to check value of the counter xfrrej --- .../features/xfrin_notify_handling.feature | 110 ++++++++++++++++++ 1 file changed, 110 insertions(+) diff --git a/tests/lettuce/features/xfrin_notify_handling.feature b/tests/lettuce/features/xfrin_notify_handling.feature index e514b83c1d..e0026abfd6 100644 --- a/tests/lettuce/features/xfrin_notify_handling.feature +++ b/tests/lettuce/features/xfrin_notify_handling.feature @@ -8,6 +8,7 @@ Feature: Xfrin incoming notify handling And wait for master stderr message AUTH_SERVER_STARTED And wait for master stderr message XFROUT_STARTED And wait for master stderr message ZONEMGR_STARTED + And wait for master stderr message STATS_STARTING And I have bind10 running with configuration xfrin/retransfer_slave_notify.conf And wait for bind10 stderr message BIND10_STARTED_CC @@ -18,6 +19,19 @@ Feature: Xfrin incoming notify handling A query for www.example.org to [::1]:47806 should have rcode NXDOMAIN + # + # Test for statistics + # + # check for initial statistics + # + When I query statistics zones of bind10 module Xfrout with cmdctl port 47804 + last bindctl output should not contain "error" + last bindctl output should not contain "example.org." + The counter notifyoutv4 for the zone _SERVER_ should be 0 + The counter notifyoutv6 for the zone _SERVER_ should be 0 + The counter xfrrej for the zone _SERVER_ should be 0 + The counter xfrreqdone for the zone _SERVER_ should be 0 + When I send bind10 with cmdctl port 47804 the command Xfrout notify example.org IN Then wait for new master stderr message XFROUT_NOTIFY_COMMAND Then wait for new bind10 stderr message AUTH_RECEIVED_NOTIFY @@ -25,5 +39,101 @@ Feature: Xfrin incoming notify handling Then wait for new bind10 stderr message XFRIN_XFR_TRANSFER_STARTED Then wait for new bind10 stderr message XFRIN_TRANSFER_SUCCESS not XFRIN_XFR_PROCESS_FAILURE Then wait for new bind10 stderr message ZONEMGR_RECEIVE_XFRIN_SUCCESS + Then wait 5 times for new master stderr message NOTIFY_OUT_SENDING_NOTIFY + Then wait for new master stderr message NOTIFY_OUT_RETRY_EXCEEDED A query for www.example.org to [::1]:47806 should have rcode NOERROR + + # + # Test for statistics + # + # check for statistics change + # + When I query statistics zones of bind10 module Xfrout with cmdctl port 47804 + last bindctl output should not contain "error" + Then wait for new master stderr message XFROUT_RECEIVED_GETSTATS_COMMAND + The counter notifyoutv4 for the zone _SERVER_ should be 0 + The counter notifyoutv4 for the zone example.org. should be 0 + The counter notifyoutv6 for the zone _SERVER_ should be 5 + The counter notifyoutv6 for the zone example.org. should be 5 + The counter xfrrej for the zone _SERVER_ should be 0 + The counter xfrrej for the zone example.org. should be 0 + The counter xfrreqdone for the zone _SERVER_ should be 1 + The counter xfrreqdone for the zone example.org. should be 1 + + # + # Test for Xfr request rejected + # + Scenario: Handle incoming notify (XFR request rejected) + Given I have bind10 running with configuration xfrin/retransfer_master.conf with cmdctl port 47804 as master + And wait for master stderr message BIND10_STARTED_CC + And wait for master stderr message CMDCTL_STARTED + And wait for master stderr message AUTH_SERVER_STARTED + And wait for master stderr message XFROUT_STARTED + And wait for master stderr message ZONEMGR_STARTED + And wait for master stderr message STATS_STARTING + + And I have bind10 running with configuration xfrin/retransfer_slave_notify.conf + And wait for bind10 stderr message BIND10_STARTED_CC + And wait for bind10 stderr message CMDCTL_STARTED + And wait for bind10 stderr message AUTH_SERVER_STARTED + And wait for bind10 stderr message XFRIN_STARTED + And wait for bind10 stderr message ZONEMGR_STARTED + + A query for www.example.org to [::1]:47806 should have rcode NXDOMAIN + + # + # Test1 for statistics + # + # check for initial statistics + # + When I query statistics zones of bind10 module Xfrout with cmdctl port 47804 + last bindctl output should not contain "error" + last bindctl output should not contain "example.org." + The counter notifyoutv4 for the zone _SERVER_ should be 0 + The counter notifyoutv6 for the zone _SERVER_ should be 0 + The counter xfrrej for the zone _SERVER_ should be 0 + The counter xfrreqdone for the zone _SERVER_ should be 0 + + # + # set transfer_acl rejection + # Local xfr requests from Xfrin module would be rejected here. + # + When I send bind10 the following commands with cmdctl port 47804 + """ + config set Xfrout/zone_config[0]/transfer_acl [{"action": "REJECT", "from": "::1"}] + config commit + """ + last bindctl output should not contain Error + + When I send bind10 with cmdctl port 47804 the command Xfrout notify example.org IN + Then wait for new master stderr message XFROUT_NOTIFY_COMMAND + Then wait for new bind10 stderr message AUTH_RECEIVED_NOTIFY + Then wait for new bind10 stderr message ZONEMGR_RECEIVE_NOTIFY + Then wait for new bind10 stderr message XFRIN_XFR_TRANSFER_STARTED + Then wait for new bind10 stderr message XFRIN_XFR_TRANSFER_PROTOCOL_ERROR not XFRIN_XFR_TRANSFER_STARTED + Then wait for new bind10 stderr message ZONEMGR_RECEIVE_XFRIN_FAILED not ZONEMGR_RECEIVE_XFRIN_SUCCESS + Then wait 5 times for new master stderr message NOTIFY_OUT_SENDING_NOTIFY + Then wait for new master stderr message NOTIFY_OUT_RETRY_EXCEEDED + + A query for www.example.org to [::1]:47806 should have rcode NXDOMAIN + + # + # Test2 for statistics + # + # check for statistics change + # + When I query statistics zones of bind10 module Xfrout with cmdctl port 47804 + last bindctl output should not contain "error" + The counter notifyoutv4 for the zone _SERVER_ should be 0 + The counter notifyoutv4 for the zone example.org. should be 0 + The counter notifyoutv6 for the zone _SERVER_ should be 5 + The counter notifyoutv6 for the zone example.org. should be 5 + # The counts of rejection would be between 1 and 2. They are not + # fixed. It would depend on timing or the platform. + The counter xfrrej for the zone _SERVER_ should be greater than 0 + The counter xfrrej for the zone _SERVER_ should be less than 3 + The counter xfrrej for the zone example.org. should be greater than 0 + The counter xfrrej for the zone example.org. should be less than 3 + The counter xfrreqdone for the zone _SERVER_ should be 0 + The counter xfrreqdone for the zone example.org. should be 0 From 302762af537cb1a570f1d803702495b2fea2a98f Mon Sep 17 00:00:00 2001 From: Naoki Kambe Date: Fri, 17 Aug 2012 15:12:23 +0900 Subject: [PATCH 010/199] [2158] updated the manpage of b10-xfrout - added statistics counters and their descriptions - added description about the zone name "_SERVER_" --- src/bin/xfrout/b10-xfrout.xml | 48 +++++++++++++++++++++++++++++++++++ 1 file changed, 48 insertions(+) diff --git a/src/bin/xfrout/b10-xfrout.xml b/src/bin/xfrout/b10-xfrout.xml index f79a42d08f..4415cffb78 100644 --- a/src/bin/xfrout/b10-xfrout.xml +++ b/src/bin/xfrout/b10-xfrout.xml @@ -153,6 +153,54 @@ + + STATISTICS DATA + + + The statistics data collected by the b10-xfrout + daemon for Xfrout include: + + + + + + notifyoutv4 + + Number of IPv4 notifies per zone name sent out from Xfrout + + + + + notifyoutv6 + + Number of IPv6 notifies per zone name sent out from Xfrout + + + + + xfrrej + + Number of XFR requests per zone name rejected by Xfrout + + + + + xfrreqdone + + Number of requested zone transfers per zone name completed + + + + + + + In per-zone counters the special zone name '_SERVER_' exists. It doesn't + mean a specific zone. It represents an entire server and its value means + a total count of all zones. + + + + - - + + DHCP Performance Guide @@ -37,7 +37,10 @@ Tomasz Mrugalski - + + Marcin + Siodelski + BIND 10 is a framework that features Domain Name System (DNS) suite and Dynamic Host Configuration Protocol (DHCP) @@ -586,9 +589,252 @@ SQLite version: 3.7.9sourceid version is 2011-11-01 00:52:41 c7c6050ef060877ebe7 perfdhcp - - TODO: Write something about perfdhcp here. - - +
+ Purpose + + There is a growing need to evaluate performance of DHCP servers in + different traffic conditions to understand their bottle necks. + This helps to elimante bugs in existing DHCP software as well + as make informed decisions regarding new DHCP software designs + to significantly improve its performance. The perfdhcp tool has + been created to fill the gap in performance measurement capabilities + mostly. However, the number of implemented features and parameters + exposed to the user make this tool useful for functional testing as + well. + +
+
+ Key features + + The perfdhcp exposes the number of command line parameters to + control DHCP message exchanges. Currently they fall back to + the following categories: + + + + Rate control - control how many DHCP exchanges + are initiated within a period of time. Tool can also simulate + best effort conditions where it attempts to start as many + exchanges as possible. + + + + + Test exit specifiers - control the conditions when test + completes including number of initiated exchanges, test period or + maximum number of dropped packets. + + + + + Packet templates - specify files containing packet templates that + are used by perfdhcp to create custom DHCP messages instead of + default. Tool also allows to specify number of values indicating + offsets of variable values within a packet that are modified in + flight by the tool. + + + + + Reporting - for each test produce the set of performance data + including achieved packet exchange rate (server performance). + There is also a number of diagnostic selectors available that + enable periodic (intermediate) reporting, packet timestamps + printing and detailed information about perfdhcp internal + states (for debugging). + + + + + Different mode of operations - specify DHCP version used + (v4 or v6), 2-way or 4-way exchanges, use Rapid Commit option + for DHCPv6. + + + + + IP layer options - specify local/remote address, local interface + and local port to be used for communication with DHCP server. + + + + +
+ +
+ Command line options + + The following command line options may be used with perfdhcp tool. + This summary also presents its possible exit codes as well as + error counters printed along when test is complete: + $ ./perfdhcp -h +] [-t] [-R] [-b] + [-n] [-p] [-d] [-D] + [-l] [-P] [-a] + [-L] [-s] [-i] [-B] [-c] [-1] + [-T] [-X] [-O] [-S] [-I] + [-x] [-w] [server] + +The [server] argument is the name/address of the DHCP server to +contact. For DHCPv4 operation, exchanges are initiated by +transmitting a DHCP DISCOVER to this address. + +For DHCPv6 operation, exchanges are initiated by transmitting a DHCP +SOLICIT to this address. In the DHCPv6 case, the special name 'all' +can be used to refer to All_DHCP_Relay_Agents_and_Servers (the +multicast address FF02::1:2), or the special name 'servers' to refer +to All_DHCP_Servers (the multicast address FF05::1:3). The [server] +argument is optional only in the case that -l is used to specify an +interface, in which case [server] defaults to 'all'. + +The default is to perform a single 4-way exchange, effectively pinging +the server. +The -r option is used to set up a performance test, without +it exchanges are initiated as fast as possible. + +Options: +-1: Take the server-ID option from the first received message. +-4: DHCPv4 operation (default). This is incompatible with the -6 option. +-6: DHCPv6 operation. This is incompatible with the -4 option. +-a: When the target sending rate is not yet reached, + control how many exchanges are initiated before the next pause. +-b: The base mac, duid, IP, etc, used to simulate different + clients. This can be specified multiple times, each instance is + in the = form, for instance: + (and default) mac=00:0c:01:02:03:04. +-d: Specify the time after which a request is treated as + having been lost. The value is given in seconds and may contain a + fractional component. The default is 1 second. +-E: Offset of the (DHCPv4) secs field / (DHCPv6) + elapsed-time option in the (second/request) template. + The value 0 disables it. +-h: Print this help. +-i: Do only the initial part of an exchange: DO or SA, depending on + whether -6 is given. +-I: Offset of the (DHCPv4) IP address in the requested-IP + option / (DHCPv6) IA_NA option in the (second/request) template. +-l: For DHCPv4 operation, specify the local + hostname/address to use when communicating with the server. By + default, the interface address through which traffic would + normally be routed to the server is used. + For DHCPv6 operation, specify the name of the network interface + via which exchanges are initiated. +-L: Specify the local port to use + (the value 0 means to use the default). +-O: Offset of the last octet to randomize in the template. +-P: Initiate first exchanges back to back at startup. +-r: Initiate DORA/SARR (or if -i is given, DO/SA) + exchanges per second. A periodic report is generated showing the + number of exchanges which were not completed, as well as the + average response latency. The program continues until + interrupted, at which point a final report is generated. +-R: Specify how many different clients are used. With 1 + (the default), all requests seem to come from the same client. +-s: Specify the seed for randomization, making it repeatable. +-S: Offset of the server-ID option in the + (second/request) template. +-T: The name of a file containing the template to use + as a stream of hexadecimal digits. +-v: Report the version number of this program. +-w: Command to call with start/stop at the beginning/end of + the program. +-x: Include extended diagnostics in the output. + is a string of single-keywords specifying + the operations for which verbose output is desired. The selector + keyletters are: + * 'a': print the decoded command line arguments + * 'e': print the exit reason + * 'i': print rate processing details + * 'r': print randomization details + * 's': print first server-id + * 't': when finished, print timers of all successful exchanges + * 'T': when finished, print templates +-X: Transaction ID (aka. xid) offset in the template. + +DHCPv4 only options: +-B: Force broadcast handling. + +DHCPv6 only options: +-c: Add a rapid commit option (exchanges will be SA). + +The remaining options are used only in conjunction with -r: + +-D: Abort the test if more than requests have + been dropped. Use -D0 to abort if even a single request has been + dropped. If includes the suffix '%', it specifies a + maximum percentage of requests that may be dropped before abort. + In this case, testing of the threshold begins after 10 requests + have been expected to be received. +-n: Initiate transactions. No report is + generated until all transactions have been initiated/waited-for, + after which a report is generated and the program terminates. +-p: Send requests for the given test period, which is + specified in the same manner as -d. This can be used as an + alternative to -n, or both options can be given, in which case the + testing is completed when either limit is reached. +-t: Delay in seconds between two periodic reports. + +Errors: +- tooshort: received a too short message +- orphans: received a message which doesn't match an exchange + (duplicate, late or not related) +- locallimit: reached to local system limits when sending a message. + +Exit status: +The exit status is: +0 on complete success. +1 for a general error. +2 if an error is found in the command line arguments. +3 if there are no general failures in operation, but one or more + exchanges are not successfully completed. +]]> + + +
+
+ Running a test + + In order to run performance test at least two separate systems + have to be installed: client and server. The first one has to have + perfdhcp tool installed, the latter has to have DHCP server + running (v4 or v6). If only single system is available the client + and server can be run on virtual machines (running on the same + phisical system) but in this case performance data may be heavily + impacted by the performance of VMs. + + + The DHCP operates on low port numbers (67 for DHCPv4 relays and + 547 for DHCPv6) running perfdhcp with non-root priviliges will + usually result in the error message similar to this: + $./perfdhcp -4 -l eth3 -r 100 all +Error running perfdhcp: Failed to bind socket 3 to 172.16.1.2/port=67 + + perfdhcp exposes '-L' command line option that + imposes use of custom local port and the following command line will + work: + $./perfdhcp -4 -l eth3 -r 100 -L 10067 all + but in the standard configuration no responses will be received + from the ISC DHCP server because server responds to default relay + port 67. + + + Alternative way to overcome the issue with lack of privileges + to use default DHCP port number is to run perfdhcp as root. + + + + In this section the perfdhcp command line options examples + are presented as a quick start guide for new users. For the + detailed list of command line options refer to + . + + + + + + +
+
From 32f6a8736328b1d532d4ebdcbd15b10ca1815db0 Mon Sep 17 00:00:00 2001 From: Jelte Jansen Date: Tue, 4 Sep 2012 19:17:27 +0200 Subject: [PATCH 113/199] [2109] ZoneFinder for the improved memory datasource Moved out the ZoneFinder-specific classes and code from memory_datasrc.[h|cc] into memory/zonefinder.[h|cc] The new code is mostly similar to the original, but using the new data structures instead of the old. It is also no longer templated (not necessary anymore) and no longer uses the pimpl (also no longer relevant), so it should be somewhat easier to read and modify. --- src/lib/datasrc/Makefile.am | 4 +- src/lib/datasrc/memory/Makefile.am | 1 + src/lib/datasrc/memory/benchmarks/Makefile.am | 3 + src/lib/datasrc/memory/tests/Makefile.am | 3 + src/lib/datasrc/memory/tests/run_unittests.cc | 3 + .../memory/tests/zone_finder_unittest.cc | 972 ++++++++++++++++++ src/lib/datasrc/memory/treenode_rrset.h | 2 + src/lib/datasrc/memory/zone_data.h | 1 + src/lib/datasrc/memory/zone_finder.cc | 412 ++++++++ src/lib/datasrc/memory/zone_finder.h | 93 ++ src/lib/datasrc/tests/faked_nsec3.cc | 2 + 11 files changed, 1494 insertions(+), 2 deletions(-) create mode 100644 src/lib/datasrc/memory/tests/zone_finder_unittest.cc create mode 100644 src/lib/datasrc/memory/zone_finder.cc create mode 100644 src/lib/datasrc/memory/zone_finder.h diff --git a/src/lib/datasrc/Makefile.am b/src/lib/datasrc/Makefile.am index d1e113f9c5..aa93b774ac 100644 --- a/src/lib/datasrc/Makefile.am +++ b/src/lib/datasrc/Makefile.am @@ -1,4 +1,4 @@ -SUBDIRS = memory . tests +SUBDIRS = . memory tests AM_CPPFLAGS = -I$(top_srcdir)/src/lib -I$(top_builddir)/src/lib AM_CPPFLAGS += -I$(top_srcdir)/src/lib/dns -I$(top_builddir)/src/lib/dns @@ -64,7 +64,7 @@ libb10_datasrc_la_LIBADD = $(top_builddir)/src/lib/exceptions/libb10-exceptions. libb10_datasrc_la_LIBADD += $(top_builddir)/src/lib/dns/libb10-dns++.la libb10_datasrc_la_LIBADD += $(top_builddir)/src/lib/log/libb10-log.la libb10_datasrc_la_LIBADD += $(top_builddir)/src/lib/cc/libb10-cc.la -libb10_datasrc_la_LIBADD += memory/libdatasrc_memory.la # convenience library +#libb10_datasrc_la_LIBADD += memory/libdatasrc_memory.la # convenience library libb10_datasrc_la_LIBADD += $(SQLITE_LIBS) BUILT_SOURCES = datasrc_config.h datasrc_messages.h datasrc_messages.cc diff --git a/src/lib/datasrc/memory/Makefile.am b/src/lib/datasrc/memory/Makefile.am index 1fb3fe7083..a2eb0d890d 100644 --- a/src/lib/datasrc/memory/Makefile.am +++ b/src/lib/datasrc/memory/Makefile.am @@ -16,4 +16,5 @@ libdatasrc_memory_la_SOURCES += treenode_rrset.h treenode_rrset.cc libdatasrc_memory_la_SOURCES += rdata_serialization.h rdata_serialization.cc libdatasrc_memory_la_SOURCES += zone_data.h zone_data.cc libdatasrc_memory_la_SOURCES += zone_table.h zone_table.cc +libdatasrc_memory_la_SOURCES += zone_finder.h zone_finder.cc EXTRA_DIST = rdata_serialization_priv.cc diff --git a/src/lib/datasrc/memory/benchmarks/Makefile.am b/src/lib/datasrc/memory/benchmarks/Makefile.am index f4ae3eefc5..4134aa88c4 100644 --- a/src/lib/datasrc/memory/benchmarks/Makefile.am +++ b/src/lib/datasrc/memory/benchmarks/Makefile.am @@ -13,8 +13,11 @@ noinst_PROGRAMS = rdata_reader_bench rrset_render_bench rdata_reader_bench_SOURCES = rdata_reader_bench.cc rdata_reader_bench_LDADD = $(top_builddir)/src/lib/datasrc/memory/libdatasrc_memory.la +rdata_reader_bench_LDADD += $(top_builddir)/src/lib/exceptions/libb10-exceptions.la rdata_reader_bench_LDADD += $(top_builddir)/src/lib/dns/libb10-dns++.la rrset_render_bench_SOURCES = rrset_render_bench.cc rrset_render_bench_LDADD = $(top_builddir)/src/lib/datasrc/memory/libdatasrc_memory.la +rrset_render_bench_LDADD += $(top_builddir)/src/lib/exceptions/libb10-exceptions.la +rrset_render_bench_LDADD += $(top_builddir)/src/lib/util/libb10-util.la rrset_render_bench_LDADD += $(top_builddir)/src/lib/dns/libb10-dns++.la diff --git a/src/lib/datasrc/memory/tests/Makefile.am b/src/lib/datasrc/memory/tests/Makefile.am index 334094d7b0..ba2fe703e6 100644 --- a/src/lib/datasrc/memory/tests/Makefile.am +++ b/src/lib/datasrc/memory/tests/Makefile.am @@ -24,6 +24,8 @@ run_unittests_SOURCES += domaintree_unittest.cc run_unittests_SOURCES += treenode_rrset_unittest.cc run_unittests_SOURCES += zone_table_unittest.cc run_unittests_SOURCES += zone_data_unittest.cc +run_unittests_SOURCES += zone_finder_unittest.cc +run_unittests_SOURCES += ../../tests/faked_nsec3.h ../../tests/faked_nsec3.cc run_unittests_SOURCES += memory_segment_test.h run_unittests_SOURCES += segment_object_holder_unittest.cc @@ -31,6 +33,7 @@ run_unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES) run_unittests_LDFLAGS = $(AM_LDFLAGS) $(GTEST_LDFLAGS) run_unittests_LDADD = $(builddir)/../libdatasrc_memory.la +run_unittests_LDADD += $(top_builddir)/src/lib/datasrc/libb10-datasrc.la run_unittests_LDADD += $(top_builddir)/src/lib/dns/libb10-dns++.la run_unittests_LDADD += $(top_builddir)/src/lib/util/unittests/libutil_unittests.la run_unittests_LDADD += $(top_builddir)/src/lib/util/libb10-util.la diff --git a/src/lib/datasrc/memory/tests/run_unittests.cc b/src/lib/datasrc/memory/tests/run_unittests.cc index 998667c2cb..6321976841 100644 --- a/src/lib/datasrc/memory/tests/run_unittests.cc +++ b/src/lib/datasrc/memory/tests/run_unittests.cc @@ -14,10 +14,13 @@ #include #include +#include int main(int argc, char* argv[]) { ::testing::InitGoogleTest(&argc, argv); + isc::log::initLogger(); + return (isc::util::unittests::run_all()); } diff --git a/src/lib/datasrc/memory/tests/zone_finder_unittest.cc b/src/lib/datasrc/memory/tests/zone_finder_unittest.cc new file mode 100644 index 0000000000..9bd09ab231 --- /dev/null +++ b/src/lib/datasrc/memory/tests/zone_finder_unittest.cc @@ -0,0 +1,972 @@ +// Copyright (C) 2012 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 "memory_segment_test.h" + +#include "../../tests/faked_nsec3.h" + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include + +#include + +#include +#include +#include + +#include +#include + +#include +#include + +using namespace std; +using namespace isc::dns; +using namespace isc::dns::rdata; +using namespace isc::datasrc; +using namespace isc::testutils; +using boost::shared_ptr; +using namespace isc::datasrc::test; +using namespace isc::datasrc::memory::test; +using namespace isc::datasrc::memory; + +namespace { +// Commonly used result codes (Who should write the prefix all the time) +using result::SUCCESS; +using result::EXIST; + +// Some faked NSEC3 hash values commonly used in tests and the faked NSEC3Hash +// object. +// +// For apex (example.org) +const char* const apex_hash = "0P9MHAVEQVM6T7VBL5LOP2U3T2RP3TOM"; +const char* const apex_hash_lower = "0p9mhaveqvm6t7vbl5lop2u3t2rp3tom"; +// For ns1.example.org +const char* const ns1_hash = "2T7B4G4VSA5SMI47K61MV5BV1A22BOJR"; +// For w.example.org +const char* const w_hash = "01UDEMVP1J2F7EG6JEBPS17VP3N8I58H"; +// For x.y.w.example.org (lower-cased) +const char* const xyw_hash = "2vptu5timamqttgl4luu9kg21e0aor3s"; +// For zzz.example.org. +const char* const zzz_hash = "R53BQ7CC2UVMUBFU5OCMM6PERS9TK9EN"; + +// A simple faked NSEC3 hash calculator with a dedicated creator for it. +// +// This is used in some NSEC3-related tests below. +class TestNSEC3HashCreator : public NSEC3HashCreator { + class TestNSEC3Hash : public NSEC3Hash { + private: + typedef map NSEC3HashMap; + typedef NSEC3HashMap::value_type NSEC3HashPair; + NSEC3HashMap map_; + public: + TestNSEC3Hash() { + // Build pre-defined hash + map_[Name("example.org")] = apex_hash; + map_[Name("www.example.org")] = "2S9MHAVEQVM6T7VBL5LOP2U3T2RP3TOM"; + map_[Name("xxx.example.org")] = "Q09MHAVEQVM6T7VBL5LOP2U3T2RP3TOM"; + map_[Name("yyy.example.org")] = "0A9MHAVEQVM6T7VBL5LOP2U3T2RP3TOM"; + map_[Name("x.y.w.example.org")] = + "2VPTU5TIMAMQTTGL4LUU9KG21E0AOR3S"; + map_[Name("y.w.example.org")] = "K8UDEMVP1J2F7EG6JEBPS17VP3N8I58H"; + map_[Name("w.example.org")] = w_hash; + map_[Name("zzz.example.org")] = zzz_hash; + map_[Name("smallest.example.org")] = + "00000000000000000000000000000000"; + map_[Name("largest.example.org")] = + "UUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUU"; + } + virtual string calculate(const Name& name) const { + const NSEC3HashMap::const_iterator found = map_.find(name); + if (found != map_.end()) { + return (found->second); + } + isc_throw(isc::Unexpected, "unexpected name for NSEC3 test: " + << name); + } + virtual bool match(const generic::NSEC3PARAM&) const { + return (true); + } + virtual bool match(const generic::NSEC3&) const { + return (true); + } + }; + +public: + virtual NSEC3Hash* create(const generic::NSEC3PARAM&) const { + return (new TestNSEC3Hash); + } + virtual NSEC3Hash* create(const generic::NSEC3&) const { + return (new TestNSEC3Hash); + } +}; + + +/// \brief expensive rrset converter +/// +/// converts any specialized rrset (which may not have implemented some +/// methods for efficiency) into a 'full' RRsetPtr, for easy use in test +/// checks. +/// +/// Done very inefficiently through text representation, speed should not +/// be a concern here. +ConstRRsetPtr +convertRRset(ConstRRsetPtr src) { + return (textToRRset(src->toText())); +} + +/// \brief Test fixture for the InMemoryZoneFinder class +class InMemoryZoneFinderTest : public ::testing::Test { + // A straightforward pair of textual RR(set) and a RRsetPtr variable + // to store the RRset. Used to build test data below. + struct RRsetData { + const char* const text; // textual representation of an RRset + RRsetPtr* rrset; + }; +protected: + // The following sub tests are shared by multiple test cases, changing + // the zone's DNSSEC status (unsigned, NSEC-signed or NSEC3-signed). + // expected_flags is set to either RESULT_NSEC_SIGNED or + // RESULT_NSEC3_SIGNED when it's NSEC/NSEC3 signed respectively and + // find() is expected to set the corresponding flags. + // find_options should be set to FIND_DNSSEC for NSEC-signed case when + // NSEC is expected to be returned. + void findCheck(ZoneFinder::FindResultFlags expected_flags = + ZoneFinder::RESULT_DEFAULT, + ZoneFinder::FindOptions find_options = + ZoneFinder::FIND_DEFAULT); + void emptyNodeCheck(ZoneFinder::FindResultFlags expected_flags = + ZoneFinder::RESULT_DEFAULT); + void doCancelWildcardCheck(ZoneFinder::FindResultFlags expected_flags = + ZoneFinder::RESULT_DEFAULT, + ZoneFinder::FindOptions find_options = + ZoneFinder::FIND_DEFAULT); + void anyWildcardCheck(ZoneFinder::FindResultFlags expected_flags = + ZoneFinder::RESULT_DEFAULT); + void emptyWildcardCheck(ZoneFinder::FindResultFlags expected_flags = + ZoneFinder::RESULT_DEFAULT); + void findNSECENTCheck(const Name& ent_name, + ConstRRsetPtr expected_nsec, + ZoneFinder::FindResultFlags expected_flags = + ZoneFinder::RESULT_DEFAULT); + +public: + InMemoryZoneFinderTest() : + class_(RRClass::IN()), + origin_("example.org"), + zone_data_(ZoneData::create(mem_sgmt_, origin_)), + zone_finder_(*zone_data_) + { + // Build test RRsets. Below, we construct an RRset for + // each textual RR(s) of zone_data, and assign it to the corresponding + // rr_xxx. + // Note that this contains an out-of-zone RR, and due to the + // validation check of masterLoad() used below, we cannot add SOA. + const RRsetData zone_data[] = { + {"example.org. 300 IN NS ns.example.org.", &rr_ns_}, + {"example.org. 300 IN A 192.0.2.1", &rr_a_}, + {"ns.example.org. 300 IN A 192.0.2.2", &rr_ns_a_}, + {"ns.example.org. 300 IN AAAA 2001:db8::2", &rr_ns_aaaa_}, + {"cname.example.org. 300 IN CNAME canonical.example.org", + &rr_cname_}, + {"cname.example.org. 300 IN A 192.0.2.3", &rr_cname_a_}, + {"dname.example.org. 300 IN DNAME target.example.org.", + &rr_dname_}, + {"dname.example.org. 300 IN A 192.0.2.39", &rr_dname_a_}, + {"dname.example.org. 300 IN NS ns.dname.example.org.", + &rr_dname_ns_}, + {"example.org. 300 IN DNAME example.com.", &rr_dname_apex_}, + {"child.example.org. 300 IN NS ns.child.example.org.", + &rr_child_ns_}, + {"child.example.org. 300 IN DS 12345 5 1 DEADBEEF", + &rr_child_ds_}, + {"ns.child.example.org. 300 IN A 192.0.2.153", + &rr_child_glue_}, + {"grand.child.example.org. 300 IN NS ns.grand.child.example.org.", + &rr_grandchild_ns_}, + {"ns.grand.child.example.org. 300 IN AAAA 2001:db8::253", + &rr_grandchild_glue_}, + {"dname.child.example.org. 300 IN DNAME example.com.", + &rr_child_dname_}, + {"example.com. 300 IN A 192.0.2.10", &rr_out_}, + {"*.wild.example.org. 300 IN A 192.0.2.1", &rr_wild_}, + {"*.cnamewild.example.org. 300 IN CNAME canonial.example.org.", + &rr_cnamewild_}, + {"foo.wild.example.org. 300 IN A 192.0.2.3", &rr_under_wild_}, + {"wild.*.foo.example.org. 300 IN A 192.0.2.1", &rr_emptywild_}, + {"wild.*.foo.*.bar.example.org. 300 IN A 192.0.2.1", + &rr_nested_emptywild_}, + {"*.nswild.example.org. 300 IN NS nswild.example.", &rr_nswild_}, + {"*.dnamewild.example.org. 300 IN DNAME dnamewild.example.", + &rr_dnamewild_}, + {"*.child.example.org. 300 IN A 192.0.2.1", &rr_child_wild_}, + {"bar.foo.wild.example.org. 300 IN A 192.0.2.2", &rr_not_wild_}, + {"baz.foo.wild.example.org. 300 IN A 192.0.2.3", + &rr_not_wild_another_}, + {"0P9MHAVEQVM6T7VBL5LOP2U3T2RP3TOM.example.org. 300 IN " + "NSEC3 1 1 12 aabbccdd 2T7B4G4VSA5SMI47K61MV5BV1A22BOJR A RRSIG", + &rr_nsec3_}, + {"example.org. 300 IN NSEC wild.*.foo.example.org. " + "NS SOA RRSIG NSEC DNSKEY", &rr_nsec_}, + // Together with the apex NSEC, these next NSECs make a complete + // chain in the case of the zone for the emptyNonterminal tests + // (We may want to clean up this generator code and/or masterLoad + // so that we can prepare conflicting datasets better) + {"wild.*.foo.example.org. 3600 IN NSEC ns.example.org. " + "A RRSIG NSEC", &rr_ent_nsec2_}, + {"ns.example.org. 3600 IN NSEC foo.wild.example.org. A RRSIG NSEC", + &rr_ent_nsec3_}, + {"foo.wild.example.org. 3600 IN NSEC example.org. A RRSIG NSEC", + &rr_ent_nsec4_}, + // And these are NSECs used in different tests + {"ns.example.org. 300 IN NSEC *.nswild.example.org. A AAAA NSEC", + &rr_ns_nsec_}, + {"*.wild.example.org. 300 IN NSEC foo.wild.example.org. A NSEC", + &rr_wild_nsec_}, + {NULL, NULL} + }; + + for (unsigned int i = 0; zone_data[i].text != NULL; ++i) { + *zone_data[i].rrset = textToRRset(zone_data[i].text); + } + + } + + ~InMemoryZoneFinderTest() { + // Make sure we reset the hash creator to the default + setNSEC3HashCreator(NULL); + ZoneData::destroy(mem_sgmt_, zone_data_, RRClass::IN()); + } + + // NSEC3-specific call for 'loading' data + void addZoneDataNSEC3(const ConstRRsetPtr rrset) { + // we're only adding one anyway so this is a simplified version + // base nsec3 chain of rrset rdata + // TODO: add if already exists? + assert(rrset->getType() == RRType::NSEC3()); + + const Rdata* rdata = &rrset->getRdataIterator()->getCurrent(); + const generic::NSEC3* nsec3_rdata = dynamic_cast(rdata); + NSEC3Data* nsec3_data = NSEC3Data::create(mem_sgmt_, *nsec3_rdata); + // in case we happen to be replacing, destroy old + NSEC3Data* old_data = zone_data_->setNSEC3Data(nsec3_data); + if (old_data != NULL) { + NSEC3Data::destroy(mem_sgmt_, old_data, rrset->getClass()); + } + } + + // simplified version of 'loading' data + void addZoneData(const ConstRRsetPtr rrset) { + ZoneNode* node = NULL; + + if (rrset->getType() == RRType::NSEC3()) { + return (addZoneDataNSEC3(rrset)); + } + zone_data_->insertName(mem_sgmt_, rrset->getName(), &node); + RdataSet* next_rds = node->getData(); + + if (rrset->getType() == RRType::NS() && + rrset->getName() != zone_data_->getOriginNode()->getName()) { + node->setFlag(DomainTreeNode::FLAG_CALLBACK); + } else if (rrset->getType() == RRType::DNAME()) { + node->setFlag(DomainTreeNode::FLAG_CALLBACK); + } + + RdataSet* rdataset = + RdataSet::create(mem_sgmt_, encoder_, rrset, ConstRRsetPtr()); + rdataset->next = next_rds; + node->setData(rdataset); + } + + // Some data to test with + const RRClass class_; + const Name origin_; + // The zone finder to torture by tests + MemorySegmentTest mem_sgmt_; + memory::ZoneData* zone_data_; + memory::InMemoryZoneFinder zone_finder_; + isc::datasrc::memory::RdataEncoder encoder_; + + // Placeholder for storing RRsets to be checked with rrsetsCheck() + vector actual_rrsets_; + + /* + * Some RRsets to put inside the zone. + */ + RRsetPtr + // Out of zone RRset + rr_out_, + // NS of example.org + rr_ns_, + // A of ns.example.org + rr_ns_a_, + // AAAA of ns.example.org + rr_ns_aaaa_, + // A of example.org + rr_a_; + RRsetPtr rr_cname_; // CNAME in example.org (RDATA will be added) + RRsetPtr rr_cname_a_; // for mixed CNAME + A case + RRsetPtr rr_dname_; // DNAME in example.org (RDATA will be added) + RRsetPtr rr_dname_a_; // for mixed DNAME + A case + RRsetPtr rr_dname_ns_; // for mixed DNAME + NS case + RRsetPtr rr_dname_apex_; // for mixed DNAME + NS case in the apex + RRsetPtr rr_child_ns_; // NS of a child domain (for delegation) + RRsetPtr rr_child_ds_; // DS of a child domain (for delegation, auth data) + RRsetPtr rr_child_glue_; // glue RR of the child domain + RRsetPtr rr_grandchild_ns_; // NS below a zone cut (unusual) + RRsetPtr rr_grandchild_glue_; // glue RR below a deeper zone cut + RRsetPtr rr_child_dname_; // A DNAME under NS + RRsetPtr rr_wild_; // Wildcard record + RRsetPtr rr_cnamewild_; // CNAME at a wildcard + RRsetPtr rr_emptywild_; + RRsetPtr rr_nested_emptywild_; + RRsetPtr rr_nswild_, rr_dnamewild_; + RRsetPtr rr_child_wild_; + RRsetPtr rr_under_wild_; + RRsetPtr rr_not_wild_; + RRsetPtr rr_not_wild_another_; + RRsetPtr rr_nsec3_; + RRsetPtr rr_nsec_; + RRsetPtr rr_ent_nsec2_; + RRsetPtr rr_ent_nsec3_; + RRsetPtr rr_ent_nsec4_; + RRsetPtr rr_ns_nsec_; + RRsetPtr rr_wild_nsec_; + + // A faked NSEC3 hash calculator for convenience. + // Tests that need to use the faked hashed values should call + // setNSEC3HashCreator() with a pointer to this variable at the beginning + // of the test (at least before adding any NSEC3/NSEC3PARAM RR). + TestNSEC3HashCreator nsec3_hash_creator_; + + /** + * \brief Test one find query to the zone finder. + * + * Asks a query to the zone finder and checks it does not throw and returns + * expected results. It returns nothing, it just signals failures + * to GTEST. + * + * \param name The name to ask for. + * \param rrtype The RRType to ask of. + * \param result The expected code of the result. + * \param check_answer Should a check against equality of the answer be + * done? + * \param answer The expected rrset, if any should be returned. + * \param expected_flags The expected result flags returned via find(). + * These can be tested using isWildcard() etc. + * \param zone_finder Check different InMemoryZoneFinder object than + * zone_finder_ (if NULL, uses zone_finder_) + * \param check_wild_answer Checks that the answer has the same RRs, type + * class and TTL as the eqxpected answer and that the name corresponds + * to the one searched. It is meant for checking answers for wildcard + * queries. + */ + void findTest(const Name& name, const RRType& rrtype, + ZoneFinder::Result result, + bool check_answer = true, + const ConstRRsetPtr& answer = ConstRRsetPtr(), + ZoneFinder::FindResultFlags expected_flags = + ZoneFinder::RESULT_DEFAULT, + memory::InMemoryZoneFinder* zone_finder = NULL, + ZoneFinder::FindOptions options = ZoneFinder::FIND_DEFAULT, + bool check_wild_answer = false) + { + SCOPED_TRACE("findTest for " + name.toText() + "/" + rrtype.toText()); + + if (zone_finder == NULL) { + zone_finder = &zone_finder_; + } + const ConstRRsetPtr answer_sig = answer ? answer->getRRsig() : + RRsetPtr(); // note we use the same type as of retval of getRRsig() + // The whole block is inside, because we need to check the result and + // we can't assign to FindResult + EXPECT_NO_THROW({ + ZoneFinderContextPtr find_result(zone_finder->find( + name, rrtype, options)); + // Check it returns correct answers + EXPECT_EQ(result, find_result->code); + EXPECT_EQ((expected_flags & ZoneFinder::RESULT_WILDCARD) != 0, + find_result->isWildcard()); + EXPECT_EQ((expected_flags & ZoneFinder::RESULT_NSEC_SIGNED) + != 0, find_result->isNSECSigned()); + EXPECT_EQ((expected_flags & ZoneFinder::RESULT_NSEC3_SIGNED) + != 0, find_result->isNSEC3Signed()); + if (check_answer) { + if (!answer) { + ASSERT_FALSE(find_result->rrset); + } else { + ASSERT_TRUE(find_result->rrset); + rrsetCheck(answer, convertRRset(find_result->rrset)); + if (answer_sig) { + ASSERT_TRUE(find_result->rrset->getRRsig()); + rrsetCheck(answer_sig, + find_result->rrset->getRRsig()); + } + } + } else if (check_wild_answer) { + ASSERT_NE(ConstRRsetPtr(), answer) << + "Wrong test, don't check for wild names if you expect " + "empty answer"; + ASSERT_NE(ConstRRsetPtr(), find_result->rrset) << + "No answer found"; + // Build the expected answer using the given name and + // other parameter of the base wildcard RRset. + RRsetPtr wildanswer(new RRset(name, answer->getClass(), + answer->getType(), + answer->getTTL())); + RdataIteratorPtr expectedIt(answer->getRdataIterator()); + for (; !expectedIt->isLast(); expectedIt->next()) { + wildanswer->addRdata(expectedIt->getCurrent()); + } + rrsetCheck(wildanswer, find_result->rrset); + + // Same for the RRSIG, if any. + if (answer_sig) { + ASSERT_TRUE(find_result->rrset->getRRsig()); + + RRsetPtr wildsig(new RRset(name, + answer_sig->getClass(), + RRType::RRSIG(), + answer_sig->getTTL())); + RdataIteratorPtr expectedIt( + answer_sig->getRdataIterator()); + for (; !expectedIt->isLast(); expectedIt->next()) { + wildsig->addRdata(expectedIt->getCurrent()); + } + rrsetCheck(wildsig, find_result->rrset->getRRsig()); + } + } + }); + } + /** + * \brief Calls the findAll on the finder and checks the result. + */ + void findAllTest(const Name& name, ZoneFinder::Result result, + const vector& expected_rrsets, + ZoneFinder::FindResultFlags expected_flags = + ZoneFinder::RESULT_DEFAULT, + memory::InMemoryZoneFinder* finder = NULL, + const ConstRRsetPtr &rrset_result = ConstRRsetPtr(), + ZoneFinder::FindOptions options = + ZoneFinder::FIND_DEFAULT) + { + if (finder == NULL) { + finder = &zone_finder_; + } + std::vector target; + ZoneFinderContextPtr find_result(finder->findAll(name, target, + options)); + EXPECT_EQ(result, find_result->code); + if (!rrset_result) { + EXPECT_FALSE(find_result->rrset); + } else { + ASSERT_TRUE(find_result->rrset); + rrsetCheck(rrset_result, convertRRset(find_result->rrset)); + } + EXPECT_EQ((expected_flags & ZoneFinder::RESULT_WILDCARD) != 0, + find_result->isWildcard()); + EXPECT_EQ((expected_flags & ZoneFinder::RESULT_NSEC_SIGNED) + != 0, find_result->isNSECSigned()); + EXPECT_EQ((expected_flags & ZoneFinder::RESULT_NSEC3_SIGNED) + != 0, find_result->isNSEC3Signed()); + // Convert all rrsets to 'full' ones before checking + std::vector converted_rrsets; + BOOST_FOREACH(ConstRRsetPtr cur_rrset, target) { + converted_rrsets.push_back(convertRRset(cur_rrset)); + } + rrsetsCheck(expected_rrsets.begin(), expected_rrsets.end(), + converted_rrsets.begin(), converted_rrsets.end()); + } +}; + +/** + * \brief Test InMemoryZoneFinder::InMemoryZoneFinder constructor. + * + * Takes the created zone finder and checks its properties they are the same + * as passed parameters. + */ +TEST_F(InMemoryZoneFinderTest, constructor) { + ASSERT_EQ(origin_, zone_finder_.getOrigin()); +} + +TEST_F(InMemoryZoneFinderTest, findCNAME) { + // install CNAME RR + addZoneData(rr_cname_); + + // Find A RR of the same. Should match the CNAME + findTest(rr_cname_->getName(), RRType::NS(), ZoneFinder::CNAME, true, + rr_cname_); + + // Find the CNAME itself. Should result in normal SUCCESS + findTest(rr_cname_->getName(), RRType::CNAME(), ZoneFinder::SUCCESS, true, + rr_cname_); +} + +TEST_F(InMemoryZoneFinderTest, findCNAMEUnderZoneCut) { + // There's nothing special when we find a CNAME under a zone cut + // (with FIND_GLUE_OK). The behavior is different from BIND 9, + // so we test this case explicitly. + addZoneData(rr_child_ns_); + ConstRRsetPtr rr_cname_under_cut_ = textToRRset( + "cname.child.example.org. 300 IN CNAME target.child.example.org."); + addZoneData(rr_cname_under_cut_); + findTest(Name("cname.child.example.org"), RRType::AAAA(), + ZoneFinder::CNAME, true, rr_cname_under_cut_, + ZoneFinder::RESULT_DEFAULT, NULL, ZoneFinder::FIND_GLUE_OK); +} + +// Search under a DNAME record. It should return the DNAME +TEST_F(InMemoryZoneFinderTest, findBelowDNAME) { + EXPECT_NO_THROW(addZoneData(rr_dname_)); + findTest(Name("below.dname.example.org"), RRType::A(), ZoneFinder::DNAME, + true, rr_dname_); +} + +// Search at the domain with DNAME. It should act as DNAME isn't there, DNAME +// influences only the data below (see RFC 2672, section 3) +TEST_F(InMemoryZoneFinderTest, findAtDNAME) { + EXPECT_NO_THROW(addZoneData(rr_dname_)); + EXPECT_NO_THROW(addZoneData(rr_dname_a_)); + + const Name dname_name(rr_dname_->getName()); + findTest(dname_name, RRType::A(), ZoneFinder::SUCCESS, true, rr_dname_a_); + findTest(dname_name, RRType::DNAME(), ZoneFinder::SUCCESS, true, + rr_dname_); + findTest(dname_name, RRType::TXT(), ZoneFinder::NXRRSET, true); +} + +// Try searching something that is both under NS and DNAME, without and with +// GLUE_OK mode (it should stop at the NS and DNAME respectively). +TEST_F(InMemoryZoneFinderTest, DNAMEUnderNS) { + addZoneData(rr_child_ns_); + addZoneData(rr_child_dname_); + + Name lowName("below.dname.child.example.org."); + + findTest(lowName, RRType::A(), ZoneFinder::DELEGATION, true, rr_child_ns_); + findTest(lowName, RRType::A(), ZoneFinder::DNAME, true, rr_child_dname_, + ZoneFinder::RESULT_DEFAULT, NULL, ZoneFinder::FIND_GLUE_OK); +} + +// Test adding child zones and zone cut handling +TEST_F(InMemoryZoneFinderTest, delegationNS) { + // add in-zone data + EXPECT_NO_THROW(addZoneData(rr_ns_)); + + // install a zone cut + EXPECT_NO_THROW(addZoneData(rr_child_ns_)); + + // below the zone cut + findTest(Name("www.child.example.org"), RRType::A(), + ZoneFinder::DELEGATION, true, rr_child_ns_); + + // at the zone cut + findTest(Name("child.example.org"), RRType::A(), ZoneFinder::DELEGATION, + true, rr_child_ns_); + findTest(Name("child.example.org"), RRType::NS(), ZoneFinder::DELEGATION, + true, rr_child_ns_); + + // finding NS for the apex (origin) node. This must not be confused + // with delegation due to the existence of an NS RR. + findTest(origin_, RRType::NS(), ZoneFinder::SUCCESS, true, rr_ns_); + + // unusual case of "nested delegation": the highest cut should be used. + EXPECT_NO_THROW(addZoneData(rr_grandchild_ns_)); + findTest(Name("www.grand.child.example.org"), RRType::A(), + // note: !rr_grandchild_ns_ + ZoneFinder::DELEGATION, true, rr_child_ns_); +} + +TEST_F(InMemoryZoneFinderTest, delegationWithDS) { + // Similar setup to the previous one, but with DS RR at the delegation + // point. + addZoneData(rr_ns_); + addZoneData(rr_child_ns_); + addZoneData(rr_child_ds_); + + // Normal types of query should result in delegation, but DS query + // should be considered in-zone (but only exactly at the delegation point). + findTest(Name("child.example.org"), RRType::A(), ZoneFinder::DELEGATION, + true, rr_child_ns_); + findTest(Name("child.example.org"), RRType::DS(), ZoneFinder::SUCCESS, + true, rr_child_ds_); + findTest(Name("grand.child.example.org"), RRType::DS(), + ZoneFinder::DELEGATION, true, rr_child_ns_); + + // There's nothing special for DS query at the zone apex. It would + // normally result in NXRRSET. + findTest(Name("example.org"), RRType::DS(), ZoneFinder::NXRRSET, + true, ConstRRsetPtr()); +} + +TEST_F(InMemoryZoneFinderTest, findAny) { + EXPECT_NO_THROW(addZoneData(rr_a_)); + EXPECT_NO_THROW(addZoneData(rr_ns_)); + EXPECT_NO_THROW(addZoneData(rr_child_glue_)); + + vector expected_sets; + + // origin + expected_sets.push_back(rr_a_); + expected_sets.push_back(rr_ns_); + findAllTest(origin_, ZoneFinder::SUCCESS, expected_sets); + + // out zone name + EXPECT_THROW(findAllTest(Name("example.com"), ZoneFinder::NXDOMAIN, + vector()), + OutOfZone); + + expected_sets.clear(); + expected_sets.push_back(rr_child_glue_); + findAllTest(rr_child_glue_->getName(), ZoneFinder::SUCCESS, expected_sets); + + // add zone cut + EXPECT_NO_THROW(addZoneData(rr_child_ns_)); + + // zone cut + findAllTest(rr_child_ns_->getName(), ZoneFinder::DELEGATION, + vector(), ZoneFinder::RESULT_DEFAULT, + NULL, rr_child_ns_); + + // glue for this zone cut + findAllTest(rr_child_glue_->getName(),ZoneFinder::DELEGATION, + vector(), ZoneFinder::RESULT_DEFAULT, + NULL, rr_child_ns_); +} + +TEST_F(InMemoryZoneFinderTest, glue) { + // install zone data: + // a zone cut + EXPECT_NO_THROW(addZoneData(rr_child_ns_)); + // glue for this cut + EXPECT_NO_THROW(addZoneData(rr_child_glue_)); + // a nested zone cut (unusual) + EXPECT_NO_THROW(addZoneData(rr_grandchild_ns_)); + // glue under the deeper zone cut + EXPECT_NO_THROW(addZoneData(rr_grandchild_glue_)); + + // by default glue is hidden due to the zone cut + findTest(rr_child_glue_->getName(), RRType::A(), ZoneFinder::DELEGATION, + true, rr_child_ns_); + + + // If we do it in the "glue OK" mode, we should find the exact match. + findTest(rr_child_glue_->getName(), RRType::A(), ZoneFinder::SUCCESS, true, + rr_child_glue_, ZoneFinder::RESULT_DEFAULT, NULL, + ZoneFinder::FIND_GLUE_OK); + + // glue OK + NXRRSET case + findTest(rr_child_glue_->getName(), RRType::AAAA(), ZoneFinder::NXRRSET, + true, ConstRRsetPtr(), ZoneFinder::RESULT_DEFAULT, NULL, + ZoneFinder::FIND_GLUE_OK); + + // glue OK + NXDOMAIN case + findTest(Name("www.child.example.org"), RRType::A(), + ZoneFinder::DELEGATION, true, rr_child_ns_, + ZoneFinder::RESULT_DEFAULT, NULL, ZoneFinder::FIND_GLUE_OK); + + // nested cut case. The glue should be found. + findTest(rr_grandchild_glue_->getName(), RRType::AAAA(), + ZoneFinder::SUCCESS, + true, rr_grandchild_glue_, ZoneFinder::RESULT_DEFAULT, NULL, + ZoneFinder::FIND_GLUE_OK); + + // A non-existent name in nested cut. This should result in delegation + // at the highest zone cut. + findTest(Name("www.grand.child.example.org"), RRType::TXT(), + ZoneFinder::DELEGATION, true, rr_child_ns_, + ZoneFinder::RESULT_DEFAULT, NULL, ZoneFinder::FIND_GLUE_OK); +} + +/** + * \brief Test searching. + * + * Check it finds or does not find correctly and does not throw exceptions. + * \todo This doesn't do any kind of CNAME and so on. If it isn't + * directly there, it just tells it doesn't exist. + */ +void +InMemoryZoneFinderTest::findCheck(ZoneFinder::FindResultFlags expected_flags, + ZoneFinder::FindOptions find_options) +{ + // Fill some data inside + // Now put all the data we have there. It should throw nothing + EXPECT_NO_THROW(addZoneData(rr_ns_)); + EXPECT_NO_THROW(addZoneData(rr_ns_a_)); + EXPECT_NO_THROW(addZoneData(rr_ns_aaaa_)); + EXPECT_NO_THROW(addZoneData(rr_a_)); + if ((expected_flags & ZoneFinder::RESULT_NSEC3_SIGNED) != 0) { + addZoneData(rr_nsec3_); + zone_data_->setSigned(true); + } + if ((expected_flags & ZoneFinder::RESULT_NSEC_SIGNED) != 0) { + addZoneData(rr_nsec_); + zone_data_->setSigned(true); + } + + // These two should be successful + findTest(origin_, RRType::NS(), ZoneFinder::SUCCESS, true, rr_ns_); + findTest(rr_ns_a_->getName(), RRType::A(), ZoneFinder::SUCCESS, true, + rr_ns_a_); + + // These domains don't exist. (and one is out of the zone). In an + // NSEC-signed zone with DNSSEC records requested, it should return the + // covering NSEC for the query name (the actual NSEC in the test data may + // not really "cover" it, but for the purpose of this test it's okay). + ConstRRsetPtr expected_nsec; // by default it's NULL + if ((expected_flags & ZoneFinder::RESULT_NSEC_SIGNED) != 0 && + (find_options & ZoneFinder::FIND_DNSSEC) != 0) { + expected_nsec = rr_nsec_; + } + + // There's no other name between this one and the origin, so when NSEC + // is to be returned it should be the origin NSEC. + findTest(Name("nothere.example.org"), RRType::A(), + ZoneFinder::NXDOMAIN, true, expected_nsec, expected_flags, + NULL, find_options); + + // The previous name in the zone is "ns.example.org", but it doesn't + // have an NSEC. It should be skipped and the origin NSEC will be + // returned as the "closest NSEC". + findTest(Name("nxdomain.example.org"), RRType::A(), + ZoneFinder::NXDOMAIN, true, expected_nsec, expected_flags, + NULL, find_options); + EXPECT_THROW(zone_finder_.find(Name("example.net"), RRType::A()), + OutOfZone); + + // These domain exist but don't have the provided RRType. For the latter + // one we now add its NSEC (which was delayed so that it wouldn't break + // other cases above). + findTest(origin_, RRType::AAAA(), ZoneFinder::NXRRSET, true, + expected_nsec, expected_flags, NULL, find_options); + + if ((expected_flags & ZoneFinder::RESULT_NSEC_SIGNED) != 0) { + addZoneData(rr_ns_nsec_); + if ((find_options & ZoneFinder::FIND_DNSSEC) != 0) { + expected_nsec = rr_ns_nsec_; + } + } + findTest(rr_ns_a_->getName(), RRType::NS(), ZoneFinder::NXRRSET, true, + expected_nsec, expected_flags, NULL, find_options); +} + +TEST_F(InMemoryZoneFinderTest, find) { + findCheck(); +} + +TEST_F(InMemoryZoneFinderTest, findNSEC3Signed) { + findCheck(ZoneFinder::RESULT_NSEC3_SIGNED); +} + +TEST_F(InMemoryZoneFinderTest, findNSEC3SignedWithDNSSEC) { + // For NSEC3-signed zones, specifying the DNSSEC option shouldn't change + // anything (the NSEC3_SIGNED flag is always set, and no records are + // returned for negative cases regardless). + findCheck(ZoneFinder::RESULT_NSEC3_SIGNED, ZoneFinder::FIND_DNSSEC); +} + +TEST_F(InMemoryZoneFinderTest, findNSECSigned) { + // NSEC-signed zone, without requesting DNSSEC (no NSEC should be provided) + findCheck(ZoneFinder::RESULT_NSEC_SIGNED); +} + +// Generalized test for Empty Nonterminal (ENT) cases with NSEC +void +InMemoryZoneFinderTest::findNSECENTCheck(const Name& ent_name, + ConstRRsetPtr expected_nsec, + ZoneFinder::FindResultFlags expected_flags) +{ + addZoneData(rr_emptywild_); + addZoneData(rr_under_wild_); + + // Sanity check: Should result in NXRRSET + findTest(ent_name, RRType::A(), ZoneFinder::NXRRSET, true, + ConstRRsetPtr(), expected_flags); + // Sanity check: No NSEC added yet + findTest(ent_name, RRType::A(), ZoneFinder::NXRRSET, true, + ConstRRsetPtr(), expected_flags, + NULL, ZoneFinder::FIND_DNSSEC); + + // Now add the NSEC rrs making it a 'complete' zone (in terms of NSEC, + // there are no sigs) + addZoneData(rr_nsec_); + addZoneData(rr_ent_nsec2_); + addZoneData(rr_ent_nsec3_); + addZoneData(rr_ent_nsec4_); + zone_data_->setSigned(true); + + // Should result in NXRRSET, and RESULT_NSEC_SIGNED + findTest(ent_name, RRType::A(), ZoneFinder::NXRRSET, true, + ConstRRsetPtr(), + expected_flags | ZoneFinder::RESULT_NSEC_SIGNED); + + // And check for the NSEC if DNSSEC_OK + findTest(ent_name, RRType::A(), ZoneFinder::NXRRSET, true, + expected_nsec, expected_flags | ZoneFinder::RESULT_NSEC_SIGNED, + NULL, ZoneFinder::FIND_DNSSEC); +} + +TEST_F(InMemoryZoneFinderTest,findNSECEmptyNonterminal) { + // Non-wildcard case + findNSECENTCheck(Name("wild.example.org"), rr_ent_nsec3_); +} + +TEST_F(InMemoryZoneFinderTest, DISABLED_findNSECEmptyNonterminalWildcard) { + // Wildcard case, above actual wildcard + findNSECENTCheck(Name("foo.example.org"), rr_nsec_); +} + +TEST_F(InMemoryZoneFinderTest,DISABLED_findNSECEmptyNonterminalAtWildcard) { + // Wildcard case, at actual wildcard + findNSECENTCheck(Name("bar.foo.example.org"), rr_nsec_, + ZoneFinder::RESULT_WILDCARD); +} + +TEST_F(InMemoryZoneFinderTest, findNSECSignedWithDNSSEC) { + // NSEC-signed zone, requesting DNSSEC (NSEC should be provided) + findCheck(ZoneFinder::RESULT_NSEC_SIGNED, ZoneFinder::FIND_DNSSEC); +} + +void +InMemoryZoneFinderTest::emptyNodeCheck( + ZoneFinder::FindResultFlags expected_flags) +{ + /* + * The backend RBTree for this test should look like as follows: + * example.org + * | + * baz (empty; easy case) + * / | \ + * bar | x.foo ('foo' part is empty; a bit trickier) + * bbb + * / + * aaa + */ + + // Construct the test zone + const char* const names[] = { + "bar.example.org.", "x.foo.example.org.", "aaa.baz.example.org.", + "bbb.baz.example.org.", NULL}; + for (int i = 0; names[i] != NULL; ++i) { + ConstRRsetPtr rrset = textToRRset(string(names[i]) + + " 300 IN A 192.0.2.1"); + addZoneData(rrset); + } + if ((expected_flags & ZoneFinder::RESULT_NSEC3_SIGNED) != 0) { + addZoneData(rr_nsec3_); + zone_data_->setSigned(true); + } + if ((expected_flags & ZoneFinder::RESULT_NSEC_SIGNED) != 0) { + addZoneData(rr_nsec_); + zone_data_->setSigned(true); + } + + // empty node matching, easy case: the node for 'baz' exists with + // no data. + findTest(Name("baz.example.org"), RRType::A(), ZoneFinder::NXRRSET, true, + ConstRRsetPtr(), expected_flags); + + // empty node matching, a trickier case: the node for 'foo' is part of + // "x.foo", which should be considered an empty node. + findTest(Name("foo.example.org"), RRType::A(), ZoneFinder::NXRRSET, true, + ConstRRsetPtr(), expected_flags); + + // "org" is contained in "example.org", but it shouldn't be treated as + // NXRRSET because it's out of zone. + // Note: basically we don't expect such a query to be performed (the common + // operation is to identify the best matching zone first then perform + // search it), but we shouldn't be confused even in the unexpected case. + EXPECT_THROW(zone_finder_.find(Name("org"), RRType::A()), OutOfZone); +} + +TEST_F(InMemoryZoneFinderTest, emptyNode) { + emptyNodeCheck(); +} + +TEST_F(InMemoryZoneFinderTest, emptyNodeNSEC3) { + emptyNodeCheck(ZoneFinder::RESULT_NSEC3_SIGNED); +} + +TEST_F(InMemoryZoneFinderTest, emptyNodeNSEC) { + emptyNodeCheck(ZoneFinder::RESULT_NSEC_SIGNED); +} + +// DISABLED: nsec3 will be re-added in #2118 +TEST_F(InMemoryZoneFinderTest, DISABLED_findNSEC3) { + // Set up the faked hash calculator. + setNSEC3HashCreator(&nsec3_hash_creator_); + + // Add a few NSEC3 records: + // apex (example.org.): hash=0P.. + // ns1.example.org: hash=2T.. + // w.example.org: hash=01.. + // zzz.example.org: hash=R5.. + const string apex_nsec3_text = string(apex_hash) + ".example.org." + + string(nsec3_common); + addZoneData(textToRRset(apex_nsec3_text)); + const string ns1_nsec3_text = string(ns1_hash) + ".example.org." + + string(nsec3_common); + addZoneData(textToRRset(ns1_nsec3_text)); + const string w_nsec3_text = string(w_hash) + ".example.org." + + string(nsec3_common); + addZoneData(textToRRset(w_nsec3_text)); + const string zzz_nsec3_text = string(zzz_hash) + ".example.org." + + string(nsec3_common); + addZoneData(textToRRset(zzz_nsec3_text)); + + performNSEC3Test(zone_finder_); +} + +// DISABLED: NSEC3 will be re-added in #2218 +TEST_F(InMemoryZoneFinderTest, DISABLED_findNSEC3ForBadZone) { + // Set up the faked hash calculator. + setNSEC3HashCreator(&nsec3_hash_creator_); + + // If the zone has nothing about NSEC3 (neither NSEC3 or NSEC3PARAM), + // findNSEC3() should be rejected. + EXPECT_THROW(zone_finder_.findNSEC3(Name("www.example.org"), true), + DataSourceError); + + // Only having NSEC3PARAM isn't enough + addZoneData(textToRRset("example.org. 300 IN NSEC3PARAM " + "1 0 12 aabbccdd")); + EXPECT_THROW(zone_finder_.findNSEC3(Name("www.example.org"), true), + DataSourceError); + + // Unless NSEC3 for apex is added the result in the recursive mode + // is guaranteed. + const string ns1_nsec3_text = string(ns1_hash) + ".example.org." + + string(nsec3_common); + addZoneData(textToRRset(ns1_nsec3_text)); + EXPECT_THROW(zone_finder_.findNSEC3(Name("www.example.org"), true), + DataSourceError); +} + +} diff --git a/src/lib/datasrc/memory/treenode_rrset.h b/src/lib/datasrc/memory/treenode_rrset.h index 2ad3a3c289..1da4d52aaa 100644 --- a/src/lib/datasrc/memory/treenode_rrset.h +++ b/src/lib/datasrc/memory/treenode_rrset.h @@ -252,6 +252,8 @@ private: const dns::Name* const realname_; }; +typedef boost::shared_ptr TreeNodeRRsetPtr; + } // namespace memory } // namespace datasrc } // namespace isc diff --git a/src/lib/datasrc/memory/zone_data.h b/src/lib/datasrc/memory/zone_data.h index f59268033f..b0cef47db6 100644 --- a/src/lib/datasrc/memory/zone_data.h +++ b/src/lib/datasrc/memory/zone_data.h @@ -43,6 +43,7 @@ namespace memory { typedef DomainTree ZoneTree; typedef DomainTreeNode ZoneNode; +typedef DomainTreeNodeChain ZoneNodeChain; /// \brief NSEC3 data for a DNS zone. /// diff --git a/src/lib/datasrc/memory/zone_finder.cc b/src/lib/datasrc/memory/zone_finder.cc new file mode 100644 index 0000000000..a058f35b0c --- /dev/null +++ b/src/lib/datasrc/memory/zone_finder.cc @@ -0,0 +1,412 @@ +// Copyright (C) 2012 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 +#include +#include + +#include +#include +#include +#include +#include +#include + +#include + +using namespace isc::dns; +using namespace isc::datasrc::memory; +using namespace isc::datasrc; + +namespace { + +// (temporary?) convenience function to create a treenoderrset +// given an rdataset; we should probably have some pool so these +// do not need to be allocated dynamically +// For now they are still dynamically allocated, and have a fixed rrclass (...) +TreeNodeRRsetPtr +createTreeNodeRRset(const ZoneNode* node, + const RdataSet* rdataset) +{ + return TreeNodeRRsetPtr(new TreeNodeRRset(RRClass::IN(), node, + rdataset, true)); +} + +struct FindState { + FindState(bool glue_ok) : + zonecut_node_(NULL), + dname_node_(NULL), + rrset_(TreeNodeRRsetPtr()), + glue_ok_(glue_ok) + {} + + // These will be set to a domain node of the highest delegation point, + // if any. In fact, we could use a single variable instead of both. + // But then we would need to distinquish these two cases by something + // else and it seemed little more confusing when this was written. + const ZoneNode* zonecut_node_; + const ZoneNode* dname_node_; + + // Delegation RRset (NS or DNAME), if found. + TreeNodeRRsetPtr rrset_; + + // Whether to continue search below a delegation point. + // Set at construction time. + const bool glue_ok_; +}; + +// A callback called from possible zone cut nodes and nodes with DNAME. +// This will be passed from findNode() to \c RBTree::find(). +bool cutCallback(const ZoneNode& node, FindState* state) { + // We need to look for DNAME first, there's allowed case where + // DNAME and NS coexist in the apex. DNAME is the one to notice, + // the NS is authoritative, not delegation (corner case explicitly + // allowed by section 3 of 2672) + const RdataSet* found_dname = RdataSet::find(node.getData(), RRType::DNAME()); + + if (found_dname != NULL) { + LOG_DEBUG(logger, DBG_TRACE_DETAILED, DATASRC_MEM_DNAME_ENCOUNTERED); + state->dname_node_ = &node; + state->rrset_ = createTreeNodeRRset(&node, found_dname); + return (true); + } + + // Look for NS + const RdataSet* found_ns = RdataSet::find(node.getData(), RRType::NS()); + if (found_ns != NULL) { + // We perform callback check only for the highest zone cut in the + // rare case of nested zone cuts. + if (state->zonecut_node_ != NULL) { + return (false); + } + + LOG_DEBUG(logger, DBG_TRACE_DETAILED, DATASRC_MEM_NS_ENCOUNTERED); + + // BIND 9 checks if this node is not the origin. That's probably + // because it can support multiple versions for dynamic updates + // and IXFR, and it's possible that the callback is called at + // the apex and the DNAME doesn't exist for a particular version. + // It cannot happen for us (at least for now), so we don't do + // that check. + state->zonecut_node_ = &node; + state->rrset_ = createTreeNodeRRset(&node, found_ns); + + // Unless glue is allowed the search stops here, so we return + // false; otherwise return true to continue the search. + return (!state->glue_ok_); + } + + // This case should not happen because we enable callback only + // when we add an RR searched for above. + assert(0); + // This is here to avoid warning (therefore compilation error) + // in case assert is turned off. Otherwise we could get "Control + // reached end of non-void function". + return (false); +} + +} // unnamed namespace + + +namespace isc { +namespace datasrc { +namespace memory { + +namespace { + // convenience function to fill in the final details + isc::datasrc::memory::ZoneFinderResultContext + createFindResult(const ZoneData& zone_data, + ZoneFinder::Result code, + TreeNodeRRsetPtr rrset, + const ZoneNode* node, + bool wild = false) { + ZoneFinder::FindResultFlags flags = ZoneFinder::RESULT_DEFAULT; + + if (wild) { + flags = flags | ZoneFinder::RESULT_WILDCARD; + } + if (code == ZoneFinder::NXRRSET || code == ZoneFinder::NXDOMAIN || wild) { + if (zone_data.isNSEC3Signed()) { + flags = flags | ZoneFinder::RESULT_NSEC3_SIGNED; + } else if (zone_data.isSigned()) { + flags = flags | ZoneFinder::RESULT_NSEC_SIGNED; + } + } + + return (ZoneFinderResultContext(code, rrset, flags, node)); + } +} // end anonymous namespace + +class InMemoryZoneFinder::Context : public ZoneFinder::Context { +public: + /// \brief Constructor. + /// + /// Note that we don't have a specific constructor for the findAll() case. + /// For (successful) type ANY query, found_node points to the + /// corresponding RB node, which is recorded within this specialized + /// context. + Context(ZoneFinder& finder, ZoneFinder::FindOptions options, + ZoneFinderResultContext result) : + ZoneFinder::Context(finder, options, + ResultContext(result.code, result.rrset, + result.flags)), + rrset_(result.rrset), found_node_(result.found_node) + {} + +private: + const TreeNodeRRsetPtr rrset_; + const ZoneNode* const found_node_; +}; + +boost::shared_ptr +InMemoryZoneFinder::find(const isc::dns::Name& name, + const isc::dns::RRType& type, + const FindOptions options) +{ + // call find_internal, and wrap the result in a ContextPtr + return ZoneFinderContextPtr(new Context(*this, options, + find_internal(name, + type, + NULL, + options))); +} + +boost::shared_ptr +InMemoryZoneFinder::findAll(const isc::dns::Name& name, + std::vector& target, + const FindOptions options) +{ + // call find_internal, and wrap the result in a ContextPtr + return ZoneFinderContextPtr(new Context(*this, options, + find_internal(name, + RRType::ANY(), + &target, + options))); +} + +namespace { + +TreeNodeRRsetPtr +getClosestNSEC(const ZoneData& zone_data, + ZoneNodeChain& chain, + ZoneFinder::FindOptions options) +{ + if (!zone_data.isSigned() || (options & ZoneFinder::FIND_DNSSEC) == 0 || zone_data.isNSEC3Signed()) { + return (TreeNodeRRsetPtr()); + } + + const ZoneNode* prev_node; + while ((prev_node = zone_data.getZoneTree().previousNode(chain)) != NULL) { + if (!prev_node->isEmpty()) { + const RdataSet* found = RdataSet::find(prev_node->getData(), RRType::NSEC()); + if (found != NULL) { + return (createTreeNodeRRset(prev_node, found)); + } + } + } + // This must be impossible and should be an internal bug. + // See the description at the method declaration. + assert(false); + // Even though there is an assert here, strict compilers + // will still need some return value. + return (TreeNodeRRsetPtr()); +} + +// A helper function for the NXRRSET case in find(). If the zone is +// NSEC-signed and DNSSEC records are requested, try to find NSEC +// on the given node, and return it if found; return NULL for all other +// cases. +TreeNodeRRsetPtr +getNSECForNXRRSET(const ZoneData& zone_data, + ZoneFinder::FindOptions options, + const ZoneNode* node) +{ + if (zone_data.isSigned() && + !zone_data.isNSEC3Signed() && + (options & ZoneFinder::FIND_DNSSEC) != 0) { + const RdataSet* found = RdataSet::find(node->getData(), RRType::NSEC()); + if (found != NULL) { + return (createTreeNodeRRset(node, found)); + } + } + return (TreeNodeRRsetPtr()); +} + + +class FindNodeResult { +public: + // Bitwise flags to represent supplemental information of the + // search result: + // Search resulted in a wildcard match. + static const unsigned int FIND_WILDCARD = 1; + // Search encountered a zone cut due to NS but continued to look for + // a glue. + static const unsigned int FIND_ZONECUT = 2; + + FindNodeResult(ZoneFinder::Result code_param, + ZoneNode* node_param, + TreeNodeRRsetPtr rrset_param, + unsigned int flags_param = 0) : + code(code_param), + node(node_param), + rrset(rrset_param), + flags(flags_param) + {} + const ZoneFinder::Result code; + const ZoneNode* node; + TreeNodeRRsetPtr rrset; + const unsigned int flags; +}; + +FindNodeResult findNode(const ZoneData& zone_data, + const Name& name, + ZoneNodeChain& chain, + ZoneFinder::FindOptions options) +{ + ZoneNode* node = NULL; + FindState state((options & ZoneFinder::FIND_GLUE_OK) != 0); + + const ZoneTree& tree(zone_data.getZoneTree()); + ZoneTree::Result result = tree.find(isc::dns::LabelSequence(name), + &node, chain, cutCallback, &state); + const unsigned int zonecut_flag = + (state.zonecut_node_ != NULL) ? FindNodeResult::FIND_ZONECUT : 0; + if (result == ZoneTree::EXACTMATCH) { + return (FindNodeResult(ZoneFinder::SUCCESS, node, state.rrset_, + zonecut_flag)); + } else if (result == ZoneTree::PARTIALMATCH) { + assert(node != NULL); + if (state.dname_node_ != NULL) { // DNAME + LOG_DEBUG(logger, DBG_TRACE_DATA, DATASRC_MEM_DNAME_FOUND). + arg(state.rrset_->getName()); + return (FindNodeResult(ZoneFinder::DNAME, NULL, state.rrset_)); + } + if (state.zonecut_node_ != NULL) { // DELEGATION due to NS + LOG_DEBUG(logger, DBG_TRACE_DATA, DATASRC_MEM_DELEG_FOUND). + arg(state.rrset_->getName()); + return (FindNodeResult(ZoneFinder::DELEGATION, NULL, state.rrset_)); + } + if (chain.getLastComparisonResult().getRelation() == + NameComparisonResult::SUPERDOMAIN) { // empty node, so NXRRSET + LOG_DEBUG(logger, DBG_TRACE_DATA, DATASRC_MEM_SUPER_STOP).arg(name); + return (FindNodeResult(ZoneFinder::NXRRSET, node, + getClosestNSEC(zone_data, chain, options))); + } + // TODO: wildcard (see memory_datasrc.cc:480) + // Nothing really matched. + LOG_DEBUG(logger, DBG_TRACE_DATA, DATASRC_MEM_NOT_FOUND).arg(name); + return (FindNodeResult(ZoneFinder::NXDOMAIN, node, + getClosestNSEC(zone_data, chain, options))); + } else { + // If the name is neither an exact or partial match, it is + // out of bailiwick, which is considered an error. + isc_throw(OutOfZone, name.toText() << " not in " << + zone_data.getOriginNode()->getName()); + } +} + +} // end anonymous namespace + +ZoneFinderResultContext +InMemoryZoneFinder::find_internal(const isc::dns::Name& name, + const isc::dns::RRType& type, + std::vector* target, + const FindOptions options) +{ + // Get the node. All other cases than an exact match are handled + // in findNode(). We simply construct a result structure and return. + ZoneNodeChain chain; + const FindNodeResult node_result = + findNode(zone_data_, name, chain, options); + if (node_result.code != SUCCESS) { + return (createFindResult(zone_data_, node_result.code, node_result.rrset, NULL)); + } + + const ZoneNode* node = node_result.node; + assert(node != NULL); + + // We've found an exact match, may or may not be a result of wildcard. + // TODO, ticket #2110 + // If there is an exact match but the node is empty, it's equivalent + // to NXRRSET. + if (node->isEmpty()) { + LOG_DEBUG(logger, DBG_TRACE_DATA, DATASRC_MEM_DOMAIN_EMPTY). + arg(name); + return (createFindResult(zone_data_, NXRRSET, + getClosestNSEC(zone_data_, chain, options), + node)); + } + + const RdataSet* found; + + // If the node callback is enabled, this may be a zone cut. If it + // has a NS RR, we should return a delegation, but not in the apex. + // There is one exception: the case for DS query, which should always + // be considered in-zone lookup. + if (node->getFlag(ZoneNode::FLAG_CALLBACK) && + node != zone_data_.getOriginNode() && type != RRType::DS()) { + found = RdataSet::find(node->getData(), RRType::NS()); + if (found != NULL) { + LOG_DEBUG(logger, DBG_TRACE_DATA, + DATASRC_MEM_EXACT_DELEGATION).arg(name); + // TODO: rename argument (wildcards, see #2110) + return (createFindResult(zone_data_, DELEGATION, createTreeNodeRRset(node, found), node)); + } + } + + // Handle type any query + if (target != NULL && node->getData() != NULL) { + // Empty domain will be handled as NXRRSET by normal processing + const RdataSet* cur_rds = node->getData(); + while (cur_rds != NULL) { + target->push_back(createTreeNodeRRset(node, cur_rds)); + cur_rds = cur_rds->getNext(); + } + LOG_DEBUG(logger, DBG_TRACE_DATA, DATASRC_MEM_ANY_SUCCESS). + arg(name); + return (createFindResult(zone_data_, SUCCESS, TreeNodeRRsetPtr(), node)); + } + + const RdataSet* currds = node->getData(); + while (currds != NULL) { + currds = currds->getNext(); + } + found = RdataSet::find(node->getData(), type); + if (found != NULL) { + // Good, it is here + LOG_DEBUG(logger, DBG_TRACE_DATA, DATASRC_MEM_SUCCESS).arg(name). + arg(type); + return (createFindResult(zone_data_, SUCCESS, createTreeNodeRRset(node, found), node)); + } else { + // Next, try CNAME. + found = RdataSet::find(node->getData(), RRType::CNAME()); + if (found != NULL) { + LOG_DEBUG(logger, DBG_TRACE_DATA, DATASRC_MEM_CNAME).arg(name); + return (createFindResult(zone_data_, CNAME, createTreeNodeRRset(node, found), node)); + } + } + // No exact match or CNAME. Get NSEC if necessary and return NXRRSET. + return (createFindResult(zone_data_, NXRRSET, getNSECForNXRRSET(zone_data_, options, node), node)); +} + +isc::datasrc::ZoneFinder::FindNSEC3Result +InMemoryZoneFinder::findNSEC3(const isc::dns::Name& name, bool recursive) { + (void)name; + (void)recursive; + isc_throw(isc::NotImplemented, "not completed yet! please implement me"); +} + +} +} +} diff --git a/src/lib/datasrc/memory/zone_finder.h b/src/lib/datasrc/memory/zone_finder.h new file mode 100644 index 0000000000..37a3a23311 --- /dev/null +++ b/src/lib/datasrc/memory/zone_finder.h @@ -0,0 +1,93 @@ +// Copyright (C) 2012 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. + +#ifndef DATASRC_MEMORY_ZONE_FINDER_H +#define DATASRC_MEMORY_ZONE_FINDER_H 1 + +#include +#include + +#include +#include +#include +#include + +namespace isc { +namespace datasrc { +namespace memory { + +class ZoneFinderResultContext { +public: + /// \brief Constructor + /// + /// The first three parameters correspond to those of + /// ZoneFinder::ResultContext. If node is non NULL, it specifies the + /// found RBNode in the search. + ZoneFinderResultContext(ZoneFinder::Result code_param, + TreeNodeRRsetPtr rrset_param, + ZoneFinder::FindResultFlags flags_param, + const ZoneNode* node) : + code(code_param), rrset(rrset_param), flags(flags_param), + found_node(node) + {} + + const ZoneFinder::Result code; + const TreeNodeRRsetPtr rrset; + const ZoneFinder::FindResultFlags flags; + const ZoneNode* const found_node; +}; + +class InMemoryZoneFinder : boost::noncopyable, public ZoneFinder { +public: + InMemoryZoneFinder(const ZoneData& zone_data) : zone_data_(zone_data) {} + + virtual boost::shared_ptr find( + const isc::dns::Name& name, + const isc::dns::RRType& type, + const FindOptions options = FIND_DEFAULT); + + virtual boost::shared_ptr findAll( + const isc::dns::Name& name, + std::vector& target, + const FindOptions options = FIND_DEFAULT); + + virtual FindNSEC3Result + findNSEC3(const isc::dns::Name& name, bool recursive); + + virtual isc::dns::Name getOrigin() const { + return zone_data_.getOriginNode()->getName(); + } + + virtual isc::dns::RRClass getClass() const { + isc_throw(isc::NotImplemented, "this method is not relevant and should " + "probably be removed from the API"); + } + + class Context; + +private: + ZoneFinderResultContext find_internal(const isc::dns::Name& name, + const isc::dns::RRType& type, + std::vector* target, + const FindOptions options = + FIND_DEFAULT); + + const ZoneData& zone_data_; +}; + +} // namespace memory +} // namespace datasrc +} // namespace isc + +#endif // DATASRC_MEMORY_ZONE_FINDER_H diff --git a/src/lib/datasrc/tests/faked_nsec3.cc b/src/lib/datasrc/tests/faked_nsec3.cc index b702d288ba..1e499976e4 100644 --- a/src/lib/datasrc/tests/faked_nsec3.cc +++ b/src/lib/datasrc/tests/faked_nsec3.cc @@ -156,6 +156,7 @@ performNSEC3Test(ZoneFinder &finder, bool rrsigs_exist) { finder.findNSEC3(origin, false)); } +/* // Recursive mode doesn't change the result in this case. { SCOPED_TRACE("apex, recursive mode"); @@ -211,6 +212,7 @@ performNSEC3Test(ZoneFinder &finder, bool rrsigs_exist) { zzz_nsec3_text, "", finder.findNSEC3(largest_name, false)); } +*/ } } From 6db917d5a6a1f209bf6381779b6673b0a64fd909 Mon Sep 17 00:00:00 2001 From: Jelte Jansen Date: Fri, 7 Sep 2012 11:09:56 +0200 Subject: [PATCH 114/199] [2109] refactor creation of TreeNodeRRsets and make RRClass a construction-time argument --- .../memory/tests/zone_finder_unittest.cc | 2 +- src/lib/datasrc/memory/zone_finder.cc | 343 +++++++++--------- src/lib/datasrc/memory/zone_finder.h | 3 +- 3 files changed, 177 insertions(+), 171 deletions(-) diff --git a/src/lib/datasrc/memory/tests/zone_finder_unittest.cc b/src/lib/datasrc/memory/tests/zone_finder_unittest.cc index 9bd09ab231..98d1118f38 100644 --- a/src/lib/datasrc/memory/tests/zone_finder_unittest.cc +++ b/src/lib/datasrc/memory/tests/zone_finder_unittest.cc @@ -182,7 +182,7 @@ public: class_(RRClass::IN()), origin_("example.org"), zone_data_(ZoneData::create(mem_sgmt_, origin_)), - zone_finder_(*zone_data_) + zone_finder_(*zone_data_, class_) { // Build test RRsets. Below, we construct an RRset for // each textual RR(s) of zone_data, and assign it to the corresponding diff --git a/src/lib/datasrc/memory/zone_finder.cc b/src/lib/datasrc/memory/zone_finder.cc index a058f35b0c..f46415ad53 100644 --- a/src/lib/datasrc/memory/zone_finder.cc +++ b/src/lib/datasrc/memory/zone_finder.cc @@ -29,25 +29,33 @@ using namespace isc::dns; using namespace isc::datasrc::memory; using namespace isc::datasrc; -namespace { +namespace isc { +namespace datasrc { +namespace memory { +namespace { // (temporary?) convenience function to create a treenoderrset // given an rdataset; we should probably have some pool so these // do not need to be allocated dynamically // For now they are still dynamically allocated, and have a fixed rrclass (...) TreeNodeRRsetPtr createTreeNodeRRset(const ZoneNode* node, - const RdataSet* rdataset) + const RdataSet* rdataset, + const RRClass& rrclass) { - return TreeNodeRRsetPtr(new TreeNodeRRset(RRClass::IN(), node, - rdataset, true)); + if (rdataset != NULL) { + return TreeNodeRRsetPtr(new TreeNodeRRset(rrclass, node, + rdataset, true)); + } else { + return TreeNodeRRsetPtr(); + } } struct FindState { FindState(bool glue_ok) : zonecut_node_(NULL), dname_node_(NULL), - rrset_(TreeNodeRRsetPtr()), + rrset_(NULL), glue_ok_(glue_ok) {} @@ -59,7 +67,8 @@ struct FindState { const ZoneNode* dname_node_; // Delegation RRset (NS or DNAME), if found. - TreeNodeRRsetPtr rrset_; + //TreeNodeRRsetPtr rrset_; + const RdataSet* rrset_; // Whether to continue search below a delegation point. // Set at construction time. @@ -78,7 +87,7 @@ bool cutCallback(const ZoneNode& node, FindState* state) { if (found_dname != NULL) { LOG_DEBUG(logger, DBG_TRACE_DETAILED, DATASRC_MEM_DNAME_ENCOUNTERED); state->dname_node_ = &node; - state->rrset_ = createTreeNodeRRset(&node, found_dname); + state->rrset_ = found_dname; return (true); } @@ -100,7 +109,7 @@ bool cutCallback(const ZoneNode& node, FindState* state) { // It cannot happen for us (at least for now), so we don't do // that check. state->zonecut_node_ = &node; - state->rrset_ = createTreeNodeRRset(&node, found_ns); + state->rrset_ = found_ns; // Unless glue is allowed the search stops here, so we return // false; otherwise return true to continue the search. @@ -116,36 +125,153 @@ bool cutCallback(const ZoneNode& node, FindState* state) { return (false); } -} // unnamed namespace +// convenience function to fill in the final details +isc::datasrc::memory::ZoneFinderResultContext +createFindResult(const RRClass& rrclass, + const ZoneData& zone_data, + ZoneFinder::Result code, + const RdataSet* rrset, + const ZoneNode* node, + bool wild = false) { + ZoneFinder::FindResultFlags flags = ZoneFinder::RESULT_DEFAULT; - -namespace isc { -namespace datasrc { -namespace memory { - -namespace { - // convenience function to fill in the final details - isc::datasrc::memory::ZoneFinderResultContext - createFindResult(const ZoneData& zone_data, - ZoneFinder::Result code, - TreeNodeRRsetPtr rrset, - const ZoneNode* node, - bool wild = false) { - ZoneFinder::FindResultFlags flags = ZoneFinder::RESULT_DEFAULT; - - if (wild) { - flags = flags | ZoneFinder::RESULT_WILDCARD; + if (wild) { + flags = flags | ZoneFinder::RESULT_WILDCARD; + } + if (code == ZoneFinder::NXRRSET || code == ZoneFinder::NXDOMAIN || wild) { + if (zone_data.isNSEC3Signed()) { + flags = flags | ZoneFinder::RESULT_NSEC3_SIGNED; + } else if (zone_data.isSigned()) { + flags = flags | ZoneFinder::RESULT_NSEC_SIGNED; } - if (code == ZoneFinder::NXRRSET || code == ZoneFinder::NXDOMAIN || wild) { - if (zone_data.isNSEC3Signed()) { - flags = flags | ZoneFinder::RESULT_NSEC3_SIGNED; - } else if (zone_data.isSigned()) { - flags = flags | ZoneFinder::RESULT_NSEC_SIGNED; + } + + return (ZoneFinderResultContext(code, createTreeNodeRRset(node, rrset, rrclass), flags, node)); +} + +const RdataSet* +getClosestNSEC(const ZoneData& zone_data, + ZoneNodeChain& chain, + const ZoneNode** nsec_node, + ZoneFinder::FindOptions options) +{ + if (!zone_data.isSigned() || (options & ZoneFinder::FIND_DNSSEC) == 0 || zone_data.isNSEC3Signed()) { + return (NULL); + } + + const ZoneNode* prev_node; + while ((prev_node = zone_data.getZoneTree().previousNode(chain)) != NULL) { + if (!prev_node->isEmpty()) { + const RdataSet* found = RdataSet::find(prev_node->getData(), RRType::NSEC()); + if (found != NULL) { + *nsec_node = prev_node; + return (found); } } - - return (ZoneFinderResultContext(code, rrset, flags, node)); } + // This must be impossible and should be an internal bug. + // See the description at the method declaration. + assert(false); + // Even though there is an assert here, strict compilers + // will still need some return value. + return (NULL); +} + +// A helper function for the NXRRSET case in find(). If the zone is +// NSEC-signed and DNSSEC records are requested, try to find NSEC +// on the given node, and return it if found; return NULL for all other +// cases. +const RdataSet* +getNSECForNXRRSET(const ZoneData& zone_data, + ZoneFinder::FindOptions options, + const ZoneNode* node) +{ + if (zone_data.isSigned() && + !zone_data.isNSEC3Signed() && + (options & ZoneFinder::FIND_DNSSEC) != 0) { + const RdataSet* found = RdataSet::find(node->getData(), RRType::NSEC()); + if (found != NULL) { + return (found); + } + } + return (NULL); +} + + +class FindNodeResult { +public: + // Bitwise flags to represent supplemental information of the + // search result: + // Search resulted in a wildcard match. + static const unsigned int FIND_WILDCARD = 1; + // Search encountered a zone cut due to NS but continued to look for + // a glue. + static const unsigned int FIND_ZONECUT = 2; + + FindNodeResult(ZoneFinder::Result code_param, + const ZoneNode* node_param, + const RdataSet* rrset_param, + unsigned int flags_param = 0) : + code(code_param), + node(node_param), + rrset(rrset_param), + flags(flags_param) + {} + const ZoneFinder::Result code; + const ZoneNode* node; + const RdataSet* rrset; + const unsigned int flags; +}; + +FindNodeResult findNode(const ZoneData& zone_data, + const Name& name, + ZoneNodeChain& chain, + ZoneFinder::FindOptions options) +{ + ZoneNode* node = NULL; + FindState state((options & ZoneFinder::FIND_GLUE_OK) != 0); + + const ZoneTree& tree(zone_data.getZoneTree()); + ZoneTree::Result result = tree.find(isc::dns::LabelSequence(name), + &node, chain, cutCallback, &state); + const unsigned int zonecut_flag = + (state.zonecut_node_ != NULL) ? FindNodeResult::FIND_ZONECUT : 0; + if (result == ZoneTree::EXACTMATCH) { + return (FindNodeResult(ZoneFinder::SUCCESS, node, state.rrset_, + zonecut_flag)); + } else if (result == ZoneTree::PARTIALMATCH) { + assert(node != NULL); + if (state.dname_node_ != NULL) { // DNAME + LOG_DEBUG(logger, DBG_TRACE_DATA, DATASRC_MEM_DNAME_FOUND). + arg(state.dname_node_->getName()); + return (FindNodeResult(ZoneFinder::DNAME, state.dname_node_, state.rrset_)); + } + if (state.zonecut_node_ != NULL) { // DELEGATION due to NS + LOG_DEBUG(logger, DBG_TRACE_DATA, DATASRC_MEM_DELEG_FOUND). + arg(state.zonecut_node_->getName()); + return (FindNodeResult(ZoneFinder::DELEGATION, state.zonecut_node_, state.rrset_)); + } + if (chain.getLastComparisonResult().getRelation() == + NameComparisonResult::SUPERDOMAIN) { // empty node, so NXRRSET + LOG_DEBUG(logger, DBG_TRACE_DATA, DATASRC_MEM_SUPER_STOP).arg(name); + const ZoneNode* nsec_node; + const RdataSet* nsec_rds = getClosestNSEC(zone_data, chain, &nsec_node, options); + return (FindNodeResult(ZoneFinder::NXRRSET, nsec_node, nsec_rds)); + } + // TODO: wildcard (see memory_datasrc.cc:480) + // Nothing really matched. + LOG_DEBUG(logger, DBG_TRACE_DATA, DATASRC_MEM_NOT_FOUND).arg(name); + const ZoneNode* nsec_node; + const RdataSet* nsec_rds = getClosestNSEC(zone_data, chain, &nsec_node, options); + return (FindNodeResult(ZoneFinder::NXDOMAIN, nsec_node, nsec_rds)); + } else { + // If the name is neither an exact or partial match, it is + // out of bailiwick, which is considered an error. + isc_throw(OutOfZone, name.toText() << " not in " << + zone_data.getOriginNode()->getName()); + } +} + } // end anonymous namespace class InMemoryZoneFinder::Context : public ZoneFinder::Context { @@ -195,129 +321,6 @@ InMemoryZoneFinder::findAll(const isc::dns::Name& name, options))); } -namespace { - -TreeNodeRRsetPtr -getClosestNSEC(const ZoneData& zone_data, - ZoneNodeChain& chain, - ZoneFinder::FindOptions options) -{ - if (!zone_data.isSigned() || (options & ZoneFinder::FIND_DNSSEC) == 0 || zone_data.isNSEC3Signed()) { - return (TreeNodeRRsetPtr()); - } - - const ZoneNode* prev_node; - while ((prev_node = zone_data.getZoneTree().previousNode(chain)) != NULL) { - if (!prev_node->isEmpty()) { - const RdataSet* found = RdataSet::find(prev_node->getData(), RRType::NSEC()); - if (found != NULL) { - return (createTreeNodeRRset(prev_node, found)); - } - } - } - // This must be impossible and should be an internal bug. - // See the description at the method declaration. - assert(false); - // Even though there is an assert here, strict compilers - // will still need some return value. - return (TreeNodeRRsetPtr()); -} - -// A helper function for the NXRRSET case in find(). If the zone is -// NSEC-signed and DNSSEC records are requested, try to find NSEC -// on the given node, and return it if found; return NULL for all other -// cases. -TreeNodeRRsetPtr -getNSECForNXRRSET(const ZoneData& zone_data, - ZoneFinder::FindOptions options, - const ZoneNode* node) -{ - if (zone_data.isSigned() && - !zone_data.isNSEC3Signed() && - (options & ZoneFinder::FIND_DNSSEC) != 0) { - const RdataSet* found = RdataSet::find(node->getData(), RRType::NSEC()); - if (found != NULL) { - return (createTreeNodeRRset(node, found)); - } - } - return (TreeNodeRRsetPtr()); -} - - -class FindNodeResult { -public: - // Bitwise flags to represent supplemental information of the - // search result: - // Search resulted in a wildcard match. - static const unsigned int FIND_WILDCARD = 1; - // Search encountered a zone cut due to NS but continued to look for - // a glue. - static const unsigned int FIND_ZONECUT = 2; - - FindNodeResult(ZoneFinder::Result code_param, - ZoneNode* node_param, - TreeNodeRRsetPtr rrset_param, - unsigned int flags_param = 0) : - code(code_param), - node(node_param), - rrset(rrset_param), - flags(flags_param) - {} - const ZoneFinder::Result code; - const ZoneNode* node; - TreeNodeRRsetPtr rrset; - const unsigned int flags; -}; - -FindNodeResult findNode(const ZoneData& zone_data, - const Name& name, - ZoneNodeChain& chain, - ZoneFinder::FindOptions options) -{ - ZoneNode* node = NULL; - FindState state((options & ZoneFinder::FIND_GLUE_OK) != 0); - - const ZoneTree& tree(zone_data.getZoneTree()); - ZoneTree::Result result = tree.find(isc::dns::LabelSequence(name), - &node, chain, cutCallback, &state); - const unsigned int zonecut_flag = - (state.zonecut_node_ != NULL) ? FindNodeResult::FIND_ZONECUT : 0; - if (result == ZoneTree::EXACTMATCH) { - return (FindNodeResult(ZoneFinder::SUCCESS, node, state.rrset_, - zonecut_flag)); - } else if (result == ZoneTree::PARTIALMATCH) { - assert(node != NULL); - if (state.dname_node_ != NULL) { // DNAME - LOG_DEBUG(logger, DBG_TRACE_DATA, DATASRC_MEM_DNAME_FOUND). - arg(state.rrset_->getName()); - return (FindNodeResult(ZoneFinder::DNAME, NULL, state.rrset_)); - } - if (state.zonecut_node_ != NULL) { // DELEGATION due to NS - LOG_DEBUG(logger, DBG_TRACE_DATA, DATASRC_MEM_DELEG_FOUND). - arg(state.rrset_->getName()); - return (FindNodeResult(ZoneFinder::DELEGATION, NULL, state.rrset_)); - } - if (chain.getLastComparisonResult().getRelation() == - NameComparisonResult::SUPERDOMAIN) { // empty node, so NXRRSET - LOG_DEBUG(logger, DBG_TRACE_DATA, DATASRC_MEM_SUPER_STOP).arg(name); - return (FindNodeResult(ZoneFinder::NXRRSET, node, - getClosestNSEC(zone_data, chain, options))); - } - // TODO: wildcard (see memory_datasrc.cc:480) - // Nothing really matched. - LOG_DEBUG(logger, DBG_TRACE_DATA, DATASRC_MEM_NOT_FOUND).arg(name); - return (FindNodeResult(ZoneFinder::NXDOMAIN, node, - getClosestNSEC(zone_data, chain, options))); - } else { - // If the name is neither an exact or partial match, it is - // out of bailiwick, which is considered an error. - isc_throw(OutOfZone, name.toText() << " not in " << - zone_data.getOriginNode()->getName()); - } -} - -} // end anonymous namespace - ZoneFinderResultContext InMemoryZoneFinder::find_internal(const isc::dns::Name& name, const isc::dns::RRType& type, @@ -330,7 +333,7 @@ InMemoryZoneFinder::find_internal(const isc::dns::Name& name, const FindNodeResult node_result = findNode(zone_data_, name, chain, options); if (node_result.code != SUCCESS) { - return (createFindResult(zone_data_, node_result.code, node_result.rrset, NULL)); + return (createFindResult(rrclass_, zone_data_, node_result.code, node_result.rrset, node_result.node)); } const ZoneNode* node = node_result.node; @@ -343,9 +346,11 @@ InMemoryZoneFinder::find_internal(const isc::dns::Name& name, if (node->isEmpty()) { LOG_DEBUG(logger, DBG_TRACE_DATA, DATASRC_MEM_DOMAIN_EMPTY). arg(name); - return (createFindResult(zone_data_, NXRRSET, - getClosestNSEC(zone_data_, chain, options), - node)); + const ZoneNode* nsec_node; + const RdataSet* nsec_rds = getClosestNSEC(zone_data_, chain, &nsec_node, options); + return (createFindResult(rrclass_, zone_data_, NXRRSET, + nsec_rds, + nsec_node)); } const RdataSet* found; @@ -361,7 +366,7 @@ InMemoryZoneFinder::find_internal(const isc::dns::Name& name, LOG_DEBUG(logger, DBG_TRACE_DATA, DATASRC_MEM_EXACT_DELEGATION).arg(name); // TODO: rename argument (wildcards, see #2110) - return (createFindResult(zone_data_, DELEGATION, createTreeNodeRRset(node, found), node)); + return (createFindResult(rrclass_, zone_data_, DELEGATION, found, node)); } } @@ -370,12 +375,12 @@ InMemoryZoneFinder::find_internal(const isc::dns::Name& name, // Empty domain will be handled as NXRRSET by normal processing const RdataSet* cur_rds = node->getData(); while (cur_rds != NULL) { - target->push_back(createTreeNodeRRset(node, cur_rds)); + target->push_back(createTreeNodeRRset(node, cur_rds, rrclass_)); cur_rds = cur_rds->getNext(); } LOG_DEBUG(logger, DBG_TRACE_DATA, DATASRC_MEM_ANY_SUCCESS). arg(name); - return (createFindResult(zone_data_, SUCCESS, TreeNodeRRsetPtr(), node)); + return (createFindResult(rrclass_, zone_data_, SUCCESS, NULL, node)); } const RdataSet* currds = node->getData(); @@ -387,17 +392,17 @@ InMemoryZoneFinder::find_internal(const isc::dns::Name& name, // Good, it is here LOG_DEBUG(logger, DBG_TRACE_DATA, DATASRC_MEM_SUCCESS).arg(name). arg(type); - return (createFindResult(zone_data_, SUCCESS, createTreeNodeRRset(node, found), node)); + return (createFindResult(rrclass_, zone_data_, SUCCESS, found, node)); } else { // Next, try CNAME. found = RdataSet::find(node->getData(), RRType::CNAME()); if (found != NULL) { LOG_DEBUG(logger, DBG_TRACE_DATA, DATASRC_MEM_CNAME).arg(name); - return (createFindResult(zone_data_, CNAME, createTreeNodeRRset(node, found), node)); + return (createFindResult(rrclass_, zone_data_, CNAME, found, node)); } } // No exact match or CNAME. Get NSEC if necessary and return NXRRSET. - return (createFindResult(zone_data_, NXRRSET, getNSECForNXRRSET(zone_data_, options, node), node)); + return (createFindResult(rrclass_, zone_data_, NXRRSET, getNSECForNXRRSET(zone_data_, options, node), node)); } isc::datasrc::ZoneFinder::FindNSEC3Result @@ -407,6 +412,6 @@ InMemoryZoneFinder::findNSEC3(const isc::dns::Name& name, bool recursive) { isc_throw(isc::NotImplemented, "not completed yet! please implement me"); } -} -} -} +} // namespace memory +} // namespace datasrc +} // namespace isc diff --git a/src/lib/datasrc/memory/zone_finder.h b/src/lib/datasrc/memory/zone_finder.h index 37a3a23311..f80ab1f99d 100644 --- a/src/lib/datasrc/memory/zone_finder.h +++ b/src/lib/datasrc/memory/zone_finder.h @@ -50,7 +50,7 @@ public: class InMemoryZoneFinder : boost::noncopyable, public ZoneFinder { public: - InMemoryZoneFinder(const ZoneData& zone_data) : zone_data_(zone_data) {} + InMemoryZoneFinder(const ZoneData& zone_data, const isc::dns::RRClass& rrclass) : zone_data_(zone_data), rrclass_(rrclass) {} virtual boost::shared_ptr find( const isc::dns::Name& name, @@ -84,6 +84,7 @@ private: FIND_DEFAULT); const ZoneData& zone_data_; + const isc::dns::RRClass& rrclass_; }; } // namespace memory From 6b7f1283d0de285cff60ecc25b6901715a6f104c Mon Sep 17 00:00:00 2001 From: Jelte Jansen Date: Fri, 7 Sep 2012 15:26:18 +0200 Subject: [PATCH 115/199] [2109] clean up for new in-memory ZoneFinder layout, documentation, etc. --- .../memory/tests/zone_finder_unittest.cc | 38 +--- src/lib/datasrc/memory/zone_finder.cc | 184 +++++++++++++++--- src/lib/datasrc/memory/zone_finder.h | 52 ++++- 3 files changed, 202 insertions(+), 72 deletions(-) diff --git a/src/lib/datasrc/memory/tests/zone_finder_unittest.cc b/src/lib/datasrc/memory/tests/zone_finder_unittest.cc index 98d1118f38..15fec597d8 100644 --- a/src/lib/datasrc/memory/tests/zone_finder_unittest.cc +++ b/src/lib/datasrc/memory/tests/zone_finder_unittest.cc @@ -13,40 +13,17 @@ // PERFORMANCE OF THIS SOFTWARE. #include "memory_segment_test.h" - #include "../../tests/faked_nsec3.h" - -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include #include -#include - #include -#include - -#include -#include -#include - -#include -#include - #include #include +#include + +#include + using namespace std; using namespace isc::dns; using namespace isc::dns::rdata; @@ -266,14 +243,13 @@ public: } // NSEC3-specific call for 'loading' data + // This needs to be updated and checked when implementing #2118 void addZoneDataNSEC3(const ConstRRsetPtr rrset) { - // we're only adding one anyway so this is a simplified version - // base nsec3 chain of rrset rdata - // TODO: add if already exists? assert(rrset->getType() == RRType::NSEC3()); const Rdata* rdata = &rrset->getRdataIterator()->getCurrent(); - const generic::NSEC3* nsec3_rdata = dynamic_cast(rdata); + const generic::NSEC3* nsec3_rdata = + dynamic_cast(rdata); NSEC3Data* nsec3_data = NSEC3Data::create(mem_sgmt_, *nsec3_rdata); // in case we happen to be replacing, destroy old NSEC3Data* old_data = zone_data_->setNSEC3Data(nsec3_data); diff --git a/src/lib/datasrc/memory/zone_finder.cc b/src/lib/datasrc/memory/zone_finder.cc index f46415ad53..994b0f98f3 100644 --- a/src/lib/datasrc/memory/zone_finder.cc +++ b/src/lib/datasrc/memory/zone_finder.cc @@ -34,16 +34,23 @@ namespace datasrc { namespace memory { namespace { -// (temporary?) convenience function to create a treenoderrset -// given an rdataset; we should probably have some pool so these -// do not need to be allocated dynamically -// For now they are still dynamically allocated, and have a fixed rrclass (...) +/// Creates a TreeNodeRRsetPtr for the given RdataSet at the given Node, for +/// the given RRClass +/// +/// We should probably have some pool so these do not need to be allocated +/// dynamically. +/// +/// \param node The ZoneNode found by the find() calls +/// \param rdataset The RdataSet to create the RRsetPtr for +/// \param rrclass The RRClass as passed by the client +/// +/// Returns an empty TreeNodeRRsetPtr is either node or rdataset is NULL. TreeNodeRRsetPtr createTreeNodeRRset(const ZoneNode* node, const RdataSet* rdataset, const RRClass& rrclass) { - if (rdataset != NULL) { + if (node != NULL && rdataset != NULL) { return TreeNodeRRsetPtr(new TreeNodeRRset(rrclass, node, rdataset, true)); } else { @@ -51,6 +58,11 @@ createTreeNodeRRset(const ZoneNode* node, } } +/// Maintain intermediate data specific to the search context used in +/// \c find(). +/// +/// It will be passed to \c cutCallback() (see below) and record a possible +/// zone cut node and related RRset (normally NS or DNAME). struct FindState { FindState(bool glue_ok) : zonecut_node_(NULL), @@ -67,7 +79,6 @@ struct FindState { const ZoneNode* dname_node_; // Delegation RRset (NS or DNAME), if found. - //TreeNodeRRsetPtr rrset_; const RdataSet* rrset_; // Whether to continue search below a delegation point. @@ -82,7 +93,8 @@ bool cutCallback(const ZoneNode& node, FindState* state) { // DNAME and NS coexist in the apex. DNAME is the one to notice, // the NS is authoritative, not delegation (corner case explicitly // allowed by section 3 of 2672) - const RdataSet* found_dname = RdataSet::find(node.getData(), RRType::DNAME()); + const RdataSet* found_dname = RdataSet::find(node.getData(), + RRType::DNAME()); if (found_dname != NULL) { LOG_DEBUG(logger, DBG_TRACE_DETAILED, DATASRC_MEM_DNAME_ENCOUNTERED); @@ -126,6 +138,15 @@ bool cutCallback(const ZoneNode& node, FindState* state) { } // convenience function to fill in the final details +// +// Set up ZoneFinderResultContext object as a return value of find(), +// taking into account wildcard matches and DNSSEC information. We set +// the NSEC/NSEC3 flag when applicable regardless of the find option; the +// caller would simply ignore these when they didn't request DNSSEC +// related results. +// +// Also performs the conversion of node + RdataSet into a TreeNodeRRsetPtr +// isc::datasrc::memory::ZoneFinderResultContext createFindResult(const RRClass& rrclass, const ZoneData& zone_data, @@ -146,23 +167,49 @@ createFindResult(const RRClass& rrclass, } } - return (ZoneFinderResultContext(code, createTreeNodeRRset(node, rrset, rrclass), flags, node)); + return (ZoneFinderResultContext(code, createTreeNodeRRset(node, + rrset, + rrclass), + flags, node)); } +// A helper function for NSEC-signed zones. It searches the zone for +// the "closest" NSEC corresponding to the search context stored in +// node_path (it should contain sufficient information to identify the +// previous name of the query name in the zone). In some cases the +// immediate closest name may not have NSEC (when it's under a zone cut +// for glue records, or even when the zone is partly broken), so this +// method continues the search until it finds a name that has NSEC, +// and returns the one found first. Due to the prerequisite (see below), +// it should always succeed. +// +// node_path must store valid search context (in practice, it's expected +// to be set by findNode()); otherwise the underlying RBTree implementation +// throws. +// +// If the zone is not considered NSEC-signed or DNSSEC records were not +// required in the original search context (specified in options), this +// method doesn't bother to find NSEC, and simply returns NULL. So, by +// definition of "NSEC-signed", when it really tries to find an NSEC it +// should succeed; there should be one at least at the zone origin. const RdataSet* getClosestNSEC(const ZoneData& zone_data, - ZoneNodeChain& chain, + ZoneNodeChain& node_path, const ZoneNode** nsec_node, ZoneFinder::FindOptions options) { - if (!zone_data.isSigned() || (options & ZoneFinder::FIND_DNSSEC) == 0 || zone_data.isNSEC3Signed()) { + if (!zone_data.isSigned() || + (options & ZoneFinder::FIND_DNSSEC) == 0 || + zone_data.isNSEC3Signed()) { return (NULL); } const ZoneNode* prev_node; - while ((prev_node = zone_data.getZoneTree().previousNode(chain)) != NULL) { + while ((prev_node = zone_data.getZoneTree().previousNode(node_path)) + != NULL) { if (!prev_node->isEmpty()) { - const RdataSet* found = RdataSet::find(prev_node->getData(), RRType::NSEC()); + const RdataSet* found = + RdataSet::find(prev_node->getData(), RRType::NSEC()); if (found != NULL) { *nsec_node = prev_node; return (found); @@ -189,7 +236,8 @@ getNSECForNXRRSET(const ZoneData& zone_data, if (zone_data.isSigned() && !zone_data.isNSEC3Signed() && (options & ZoneFinder::FIND_DNSSEC) != 0) { - const RdataSet* found = RdataSet::find(node->getData(), RRType::NSEC()); + const RdataSet* found = RdataSet::find(node->getData(), + RRType::NSEC()); if (found != NULL) { return (found); } @@ -197,7 +245,7 @@ getNSECForNXRRSET(const ZoneData& zone_data, return (NULL); } - +// Structure to hold result data of the findNode() call class FindNodeResult { public: // Bitwise flags to represent supplemental information of the @@ -223,9 +271,67 @@ public: const unsigned int flags; }; +// Implementation notes: this method identifies an ZoneNode that best matches +// the give name in terms of DNS query handling. In many cases, +// DomainTree::find() will result in EXACTMATCH or PARTIALMATCH (note that +// the given name is generally expected to be contained in the zone, so +// even if it doesn't exist, it should at least match the zone origin). +// If it finds an exact match, that's obviously the best one. The partial +// match case is more complicated. +// +// We first need to consider the case where search hits a delegation point, +// either due to NS or DNAME. They are indicated as either dname_node_ or +// zonecut_node_ being non NULL. Usually at most one of them will be +// something else than NULL (it might happen both are NULL, in which case we +// consider it NOT FOUND). There's one corner case when both might be +// something else than NULL and it is in case there's a DNAME under a zone +// cut and we search in glue OK mode ‒ in that case we don't stop on the +// domain with NS and ignore it for the answer, but it gets set anyway. Then +// we find the DNAME and we need to act by it, therefore we first check for +// DNAME and then for NS. In all other cases it doesn't matter, as at least +// one of them is NULL. +// +// Next, we need to check if the ZoneTree search stopped at a node for a +// subdomain of the search name (so the comparison result that stopped the +// search is "SUPERDOMAIN"), it means the stopping node is an empty +// non-terminal node. In this case the search name is considered to exist +// but no data should be found there. +// +// (TODO: check this part when doing #2110) +// If none of above is the case, we then consider whether there's a matching +// wildcard. DomainTree::find() records the node if it encounters a +// "wildcarding" node, i.e., the immediate ancestor of a wildcard name +// (e.g., wild.example.com for *.wild.example.com), and returns it if it +// doesn't find any node that better matches the query name. In this case +// we'll check if there's indeed a wildcard below the wildcarding node. +// +// Note, first, that the wildcard is checked after the empty +// non-terminal domain case above, because if that one triggers, it +// means we should not match according to 4.3.3 of RFC 1034 (the query +// name is known to exist). +// +// Before we try to find a wildcard, we should check whether there's +// an existing node that would cancel the wildcard match. If +// DomainTree::find() stopped at a node which has a common ancestor +// with the query name, it might mean we are comparing with a +// non-wildcard node. In that case, we check which part is common. If +// we have something in common that lives below the node we got (the +// one above *), then we should cancel the match according to section +// 4.3.3 of RFC 1034 (as the name between the wildcard domain and the +// query name is known to exist). +// +// If there's no node below the wildcarding node that shares a common ancestor +// of the query name, we can conclude the wildcard is the best match. +// We'll then identify the wildcard node via an incremental search. Note that +// there's no possibility that the query name is at an empty non terminal +// node below the wildcarding node at this stage; that case should have been +// caught above. +// +// If none of the above succeeds, we conclude the name doesn't exist in +// the zone, and throw an OutOfZone exception. FindNodeResult findNode(const ZoneData& zone_data, const Name& name, - ZoneNodeChain& chain, + ZoneNodeChain& node_path, ZoneFinder::FindOptions options) { ZoneNode* node = NULL; @@ -233,7 +339,8 @@ FindNodeResult findNode(const ZoneData& zone_data, const ZoneTree& tree(zone_data.getZoneTree()); ZoneTree::Result result = tree.find(isc::dns::LabelSequence(name), - &node, chain, cutCallback, &state); + &node, node_path, cutCallback, + &state); const unsigned int zonecut_flag = (state.zonecut_node_ != NULL) ? FindNodeResult::FIND_ZONECUT : 0; if (result == ZoneTree::EXACTMATCH) { @@ -244,25 +351,34 @@ FindNodeResult findNode(const ZoneData& zone_data, if (state.dname_node_ != NULL) { // DNAME LOG_DEBUG(logger, DBG_TRACE_DATA, DATASRC_MEM_DNAME_FOUND). arg(state.dname_node_->getName()); - return (FindNodeResult(ZoneFinder::DNAME, state.dname_node_, state.rrset_)); + return (FindNodeResult(ZoneFinder::DNAME, state.dname_node_, + state.rrset_)); } if (state.zonecut_node_ != NULL) { // DELEGATION due to NS LOG_DEBUG(logger, DBG_TRACE_DATA, DATASRC_MEM_DELEG_FOUND). arg(state.zonecut_node_->getName()); - return (FindNodeResult(ZoneFinder::DELEGATION, state.zonecut_node_, state.rrset_)); + return (FindNodeResult(ZoneFinder::DELEGATION, + state.zonecut_node_, + state.rrset_)); } - if (chain.getLastComparisonResult().getRelation() == + if (node_path.getLastComparisonResult().getRelation() == NameComparisonResult::SUPERDOMAIN) { // empty node, so NXRRSET - LOG_DEBUG(logger, DBG_TRACE_DATA, DATASRC_MEM_SUPER_STOP).arg(name); + LOG_DEBUG(logger, DBG_TRACE_DATA, + DATASRC_MEM_SUPER_STOP).arg(name); const ZoneNode* nsec_node; - const RdataSet* nsec_rds = getClosestNSEC(zone_data, chain, &nsec_node, options); - return (FindNodeResult(ZoneFinder::NXRRSET, nsec_node, nsec_rds)); + const RdataSet* nsec_rds = getClosestNSEC(zone_data, + node_path, + &nsec_node, + options); + return (FindNodeResult(ZoneFinder::NXRRSET, nsec_node, + nsec_rds)); } - // TODO: wildcard (see memory_datasrc.cc:480) + // TODO: wildcard (see memory_datasrc.cc:480 and ticket #2110) // Nothing really matched. LOG_DEBUG(logger, DBG_TRACE_DATA, DATASRC_MEM_NOT_FOUND).arg(name); const ZoneNode* nsec_node; - const RdataSet* nsec_rds = getClosestNSEC(zone_data, chain, &nsec_node, options); + const RdataSet* nsec_rds = getClosestNSEC(zone_data, node_path, + &nsec_node, options); return (FindNodeResult(ZoneFinder::NXDOMAIN, nsec_node, nsec_rds)); } else { // If the name is neither an exact or partial match, it is @@ -274,6 +390,7 @@ FindNodeResult findNode(const ZoneData& zone_data, } // end anonymous namespace +// Specialization of the ZoneFinder::Context for the in-memory finder. class InMemoryZoneFinder::Context : public ZoneFinder::Context { public: /// \brief Constructor. @@ -300,7 +417,6 @@ InMemoryZoneFinder::find(const isc::dns::Name& name, const isc::dns::RRType& type, const FindOptions options) { - // call find_internal, and wrap the result in a ContextPtr return ZoneFinderContextPtr(new Context(*this, options, find_internal(name, type, @@ -313,7 +429,6 @@ InMemoryZoneFinder::findAll(const isc::dns::Name& name, std::vector& target, const FindOptions options) { - // call find_internal, and wrap the result in a ContextPtr return ZoneFinderContextPtr(new Context(*this, options, find_internal(name, RRType::ANY(), @@ -329,11 +444,12 @@ InMemoryZoneFinder::find_internal(const isc::dns::Name& name, { // Get the node. All other cases than an exact match are handled // in findNode(). We simply construct a result structure and return. - ZoneNodeChain chain; + ZoneNodeChain node_path; const FindNodeResult node_result = - findNode(zone_data_, name, chain, options); + findNode(zone_data_, name, node_path, options); if (node_result.code != SUCCESS) { - return (createFindResult(rrclass_, zone_data_, node_result.code, node_result.rrset, node_result.node)); + return (createFindResult(rrclass_, zone_data_, node_result.code, + node_result.rrset, node_result.node)); } const ZoneNode* node = node_result.node; @@ -347,7 +463,8 @@ InMemoryZoneFinder::find_internal(const isc::dns::Name& name, LOG_DEBUG(logger, DBG_TRACE_DATA, DATASRC_MEM_DOMAIN_EMPTY). arg(name); const ZoneNode* nsec_node; - const RdataSet* nsec_rds = getClosestNSEC(zone_data_, chain, &nsec_node, options); + const RdataSet* nsec_rds = getClosestNSEC(zone_data_, node_path, + &nsec_node, options); return (createFindResult(rrclass_, zone_data_, NXRRSET, nsec_rds, nsec_node)); @@ -366,7 +483,8 @@ InMemoryZoneFinder::find_internal(const isc::dns::Name& name, LOG_DEBUG(logger, DBG_TRACE_DATA, DATASRC_MEM_EXACT_DELEGATION).arg(name); // TODO: rename argument (wildcards, see #2110) - return (createFindResult(rrclass_, zone_data_, DELEGATION, found, node)); + return (createFindResult(rrclass_, zone_data_, DELEGATION, + found, node)); } } @@ -402,7 +520,9 @@ InMemoryZoneFinder::find_internal(const isc::dns::Name& name, } } // No exact match or CNAME. Get NSEC if necessary and return NXRRSET. - return (createFindResult(rrclass_, zone_data_, NXRRSET, getNSECForNXRRSET(zone_data_, options, node), node)); + return (createFindResult(rrclass_, zone_data_, NXRRSET, + getNSECForNXRRSET(zone_data_, options, node), + node)); } isc::datasrc::ZoneFinder::FindNSEC3Result diff --git a/src/lib/datasrc/memory/zone_finder.h b/src/lib/datasrc/memory/zone_finder.h index f80ab1f99d..8f2c687028 100644 --- a/src/lib/datasrc/memory/zone_finder.h +++ b/src/lib/datasrc/memory/zone_finder.h @@ -48,40 +48,74 @@ public: const ZoneNode* const found_node; }; +/// A derived zone finder class intended to be used with the memory data +/// source, using ZoneData for its contents. class InMemoryZoneFinder : boost::noncopyable, public ZoneFinder { public: - InMemoryZoneFinder(const ZoneData& zone_data, const isc::dns::RRClass& rrclass) : zone_data_(zone_data), rrclass_(rrclass) {} + /// \brief Constructor. + /// + /// Since ZoneData does not keep RRClass information, but this + /// information is needed in order to construct actual RRsets, + /// this needs to be passed here (the datasource client should + /// have this information). In the future, this may be replaced + /// by some construction to pull TreeNodeRRsets from a pool, but + /// currently, these are created dynamically with the given RRclass + /// + /// \param zone_data The ZoneData containing the zone. + /// \param rrclass The RR class of the zone + InMemoryZoneFinder(const ZoneData& zone_data, + const isc::dns::RRClass& rrclass) : + zone_data_(zone_data), + rrclass_(rrclass) + {} + /// \brief Find an RRset in the datasource virtual boost::shared_ptr find( const isc::dns::Name& name, const isc::dns::RRType& type, const FindOptions options = FIND_DEFAULT); + /// \brief Version of find that returns all types at once + /// + /// It acts the same as find, just that when the correct node is found, + /// all the RRsets are filled into the target parameter instead of being + /// returned by the result. virtual boost::shared_ptr findAll( const isc::dns::Name& name, std::vector& target, const FindOptions options = FIND_DEFAULT); + /// Look for NSEC3 for proving (non)existence of given name. + /// + /// See documentation in \c Zone. virtual FindNSEC3Result findNSEC3(const isc::dns::Name& name, bool recursive); + /// \brief Returns the origin of the zone. virtual isc::dns::Name getOrigin() const { return zone_data_.getOriginNode()->getName(); } + /// \brief Returns the RR class of the zone. virtual isc::dns::RRClass getClass() const { - isc_throw(isc::NotImplemented, "this method is not relevant and should " - "probably be removed from the API"); + return rrclass_; } - class Context; private: - ZoneFinderResultContext find_internal(const isc::dns::Name& name, - const isc::dns::RRType& type, - std::vector* target, - const FindOptions options = - FIND_DEFAULT); + /// \brief In-memory version of finder context. + /// + /// The implementation (and any specialized interface) is completely local + /// to the InMemoryZoneFinder class, so it's defined as private + class Context; + + /// Actual implementation for both find() and findAll() + ZoneFinderResultContext find_internal( + const isc::dns::Name& name, + const isc::dns::RRType& type, + std::vector* target, + const FindOptions options = + FIND_DEFAULT); const ZoneData& zone_data_; const isc::dns::RRClass& rrclass_; From 1982543450f925e2f6b3b0a029b6c9425788854e Mon Sep 17 00:00:00 2001 From: Jelte Jansen Date: Fri, 7 Sep 2012 16:38:03 +0200 Subject: [PATCH 116/199] [2109] uncomment some dead code that got committed by accident and should not have been disabled --- src/lib/datasrc/tests/faked_nsec3.cc | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/lib/datasrc/tests/faked_nsec3.cc b/src/lib/datasrc/tests/faked_nsec3.cc index 1e499976e4..b702d288ba 100644 --- a/src/lib/datasrc/tests/faked_nsec3.cc +++ b/src/lib/datasrc/tests/faked_nsec3.cc @@ -156,7 +156,6 @@ performNSEC3Test(ZoneFinder &finder, bool rrsigs_exist) { finder.findNSEC3(origin, false)); } -/* // Recursive mode doesn't change the result in this case. { SCOPED_TRACE("apex, recursive mode"); @@ -212,7 +211,6 @@ performNSEC3Test(ZoneFinder &finder, bool rrsigs_exist) { zzz_nsec3_text, "", finder.findNSEC3(largest_name, false)); } -*/ } } From ed1a5d91d8ca0826a6c6370abfa511c052b60c24 Mon Sep 17 00:00:00 2001 From: Jelte Jansen Date: Fri, 7 Sep 2012 17:20:28 +0200 Subject: [PATCH 117/199] [2151] add well-known static wildcard labelsequence --- src/lib/dns/labelsequence.h | 15 +++++++++++++++ src/lib/dns/tests/labelsequence_unittest.cc | 6 ++++++ 2 files changed, 21 insertions(+) diff --git a/src/lib/dns/labelsequence.h b/src/lib/dns/labelsequence.h index 545ce12133..b9105506c1 100644 --- a/src/lib/dns/labelsequence.h +++ b/src/lib/dns/labelsequence.h @@ -53,6 +53,14 @@ public: static const size_t MAX_SERIALIZED_LENGTH = Name::MAX_WIRE + Name::MAX_LABELS + 1; + /// + /// \name Well-known LabelSequence constants + /// + //@{ + /// Wildcard label ("*") + static const LabelSequence& WILDCARD_LABEL(); + //@} + /// \brief Constructs a LabelSequence for the given name /// /// \note The associated Name MUST remain in scope during the lifetime @@ -410,6 +418,13 @@ private: std::ostream& operator<<(std::ostream& os, const LabelSequence& label_sequence); +inline const LabelSequence& +LabelSequence::WILDCARD_LABEL() { + static const uint8_t wildcard_buf[4] = { 0x01, 0x00, 0x01, '*' }; + static const LabelSequence wild_ls(wildcard_buf); + return (wild_ls); +} + } // end namespace dns } // end namespace isc diff --git a/src/lib/dns/tests/labelsequence_unittest.cc b/src/lib/dns/tests/labelsequence_unittest.cc index 28f624aabd..06092c008e 100644 --- a/src/lib/dns/tests/labelsequence_unittest.cc +++ b/src/lib/dns/tests/labelsequence_unittest.cc @@ -1174,4 +1174,10 @@ TEST_F(ExtendableLabelSequenceTest, extendBadData) { check_equal(full_ls2, els); } +// Check the static fixed 'wildcard' LabelSequence +TEST(WildCardLabelSequence, wildcard) { + ASSERT_FALSE(LabelSequence::WILDCARD_LABEL().isAbsolute()); + ASSERT_EQ("*", LabelSequence::WILDCARD_LABEL().toText()); +} + } From 5a70502ad2679d135e26db9f3eb0a523eefd83e6 Mon Sep 17 00:00:00 2001 From: Mukund Sivaraman Date: Mon, 10 Sep 2012 06:44:25 +0530 Subject: [PATCH 118/199] [2108] Add tests for out of zone names --- .../memory/tests/memory_client_unittest.cc | 23 +++++++++++++++++++ .../datasrc/memory/tests/testdata/Makefile.am | 1 + .../testdata/example.org-out-of-zone.zone | 5 ++++ 3 files changed, 29 insertions(+) create mode 100644 src/lib/datasrc/memory/tests/testdata/example.org-out-of-zone.zone diff --git a/src/lib/datasrc/memory/tests/memory_client_unittest.cc b/src/lib/datasrc/memory/tests/memory_client_unittest.cc index fb9d339a84..18dacab4db 100644 --- a/src/lib/datasrc/memory/tests/memory_client_unittest.cc +++ b/src/lib/datasrc/memory/tests/memory_client_unittest.cc @@ -281,6 +281,15 @@ TEST_F(MemoryClientTest, loadMultipleNSEC3PARAMThrows) { // Teardown checks for memory segment leaks } +TEST_F(MemoryClientTest, loadOutOfZoneThrows) { + // Out of zone names should throw. + EXPECT_THROW(client_->load(Name("example.org"), + TEST_DATA_DIR + "/example.org-out-of-zone.zone"), + MasterLoadError); + // Teardown checks for memory segment leaks +} + TEST_F(MemoryClientTest, loadRRSIGFollowsNothing) { EXPECT_THROW(client_->load(Name("example.org"), TEST_DATA_DIR @@ -389,6 +398,20 @@ TEST_F(MemoryClientTest, addRRsetToNonExistentZoneThrows) { EXPECT_THROW(client_->add(Name("example.org"), rrset_a), DataSourceError); } +TEST_F(MemoryClientTest, addOutOfZoneThrows) { + // Out of zone names should throw. + client_->load(Name("example.org"), + TEST_DATA_DIR "/example.org-empty.zone"); + + RRsetPtr rrset_a(new RRset(Name("a.example.com"), + RRClass::IN(), RRType::A(), RRTTL(300))); + rrset_a->addRdata(rdata::in::A("192.0.2.1")); + + EXPECT_THROW(client_->add(Name("example.org"), rrset_a), + OutOfZone); + // Teardown checks for memory segment leaks +} + TEST_F(MemoryClientTest, addNullRRsetThrows) { client_->load(Name("example.org"), TEST_DATA_DIR "/example.org-rrsigs.zone"); diff --git a/src/lib/datasrc/memory/tests/testdata/Makefile.am b/src/lib/datasrc/memory/tests/testdata/Makefile.am index d52397261b..50d68b3598 100644 --- a/src/lib/datasrc/memory/tests/testdata/Makefile.am +++ b/src/lib/datasrc/memory/tests/testdata/Makefile.am @@ -17,3 +17,4 @@ EXTRA_DIST += example.org-multiple-nsec3.zone EXTRA_DIST += example.org-multiple-nsec3param.zone EXTRA_DIST += example.org-nsec3-signed.zone EXTRA_DIST += example.org-nsec3-signed-no-param.zone +EXTRA_DIST += example.org-out-of-zone.zone diff --git a/src/lib/datasrc/memory/tests/testdata/example.org-out-of-zone.zone b/src/lib/datasrc/memory/tests/testdata/example.org-out-of-zone.zone new file mode 100644 index 0000000000..e3afb74bb4 --- /dev/null +++ b/src/lib/datasrc/memory/tests/testdata/example.org-out-of-zone.zone @@ -0,0 +1,5 @@ +;; test zone file used for ZoneFinderContext tests. +;; RRSIGs are (obviouslly) faked ones for testing. + +example.org. 3600 IN SOA ns1.example.org. bugs.x.w.example.org. 75 3600 300 3600000 3600 +a.example.com. 3600 IN A 192.168.0.1 From 86d8c6421097faeb05a2abc1638c61c44913bd27 Mon Sep 17 00:00:00 2001 From: Mukund Sivaraman Date: Mon, 10 Sep 2012 07:00:20 +0530 Subject: [PATCH 119/199] [2108] Check that loading a zone with wildcard NS names throws an exception --- src/lib/datasrc/memory/tests/memory_client_unittest.cc | 9 +++++++++ src/lib/datasrc/memory/tests/testdata/Makefile.am | 1 + .../memory/tests/testdata/example.org-wildcard-ns.zone | 4 ++++ 3 files changed, 14 insertions(+) create mode 100644 src/lib/datasrc/memory/tests/testdata/example.org-wildcard-ns.zone diff --git a/src/lib/datasrc/memory/tests/memory_client_unittest.cc b/src/lib/datasrc/memory/tests/memory_client_unittest.cc index 18dacab4db..69bf5baf48 100644 --- a/src/lib/datasrc/memory/tests/memory_client_unittest.cc +++ b/src/lib/datasrc/memory/tests/memory_client_unittest.cc @@ -290,6 +290,15 @@ TEST_F(MemoryClientTest, loadOutOfZoneThrows) { // Teardown checks for memory segment leaks } +TEST_F(MemoryClientTest, loadWildcardNSThrows) { + // Wildcard NS names should throw + EXPECT_THROW(client_->load(Name("example.org"), + TEST_DATA_DIR + "/example.org-wildcard-ns.zone"), + InMemoryClient::AddError); + // Teardown checks for memory segment leaks +} + TEST_F(MemoryClientTest, loadRRSIGFollowsNothing) { EXPECT_THROW(client_->load(Name("example.org"), TEST_DATA_DIR diff --git a/src/lib/datasrc/memory/tests/testdata/Makefile.am b/src/lib/datasrc/memory/tests/testdata/Makefile.am index 50d68b3598..a674a8b887 100644 --- a/src/lib/datasrc/memory/tests/testdata/Makefile.am +++ b/src/lib/datasrc/memory/tests/testdata/Makefile.am @@ -18,3 +18,4 @@ EXTRA_DIST += example.org-multiple-nsec3param.zone EXTRA_DIST += example.org-nsec3-signed.zone EXTRA_DIST += example.org-nsec3-signed-no-param.zone EXTRA_DIST += example.org-out-of-zone.zone +EXTRA_DIST += example.org-wildcard-ns.zone diff --git a/src/lib/datasrc/memory/tests/testdata/example.org-wildcard-ns.zone b/src/lib/datasrc/memory/tests/testdata/example.org-wildcard-ns.zone new file mode 100644 index 0000000000..2933515ee9 --- /dev/null +++ b/src/lib/datasrc/memory/tests/testdata/example.org-wildcard-ns.zone @@ -0,0 +1,4 @@ +;; test zone file with wildcard NS names + +example.org. 3600 IN SOA ns1.example.org. bugs.x.w.example.org. 78 3600 300 3600000 3600 +*.example.org. 3600 IN NS ns1.example.org. From 15bdb89898addbb7149479a083af674c545755d7 Mon Sep 17 00:00:00 2001 From: Mukund Sivaraman Date: Mon, 10 Sep 2012 07:02:58 +0530 Subject: [PATCH 120/199] [2108] Check that loading a zone with wildcard DNAME names throws an exception --- src/lib/datasrc/memory/tests/memory_client_unittest.cc | 9 +++++++++ src/lib/datasrc/memory/tests/testdata/Makefile.am | 1 + .../tests/testdata/example.org-wildcard-dname.zone | 4 ++++ 3 files changed, 14 insertions(+) create mode 100644 src/lib/datasrc/memory/tests/testdata/example.org-wildcard-dname.zone diff --git a/src/lib/datasrc/memory/tests/memory_client_unittest.cc b/src/lib/datasrc/memory/tests/memory_client_unittest.cc index 69bf5baf48..64214d978d 100644 --- a/src/lib/datasrc/memory/tests/memory_client_unittest.cc +++ b/src/lib/datasrc/memory/tests/memory_client_unittest.cc @@ -299,6 +299,15 @@ TEST_F(MemoryClientTest, loadWildcardNSThrows) { // Teardown checks for memory segment leaks } +TEST_F(MemoryClientTest, loadWildcardDNAMEThrows) { + // Wildcard NS names should throw + EXPECT_THROW(client_->load(Name("example.org"), + TEST_DATA_DIR + "/example.org-wildcard-dname.zone"), + InMemoryClient::AddError); + // Teardown checks for memory segment leaks +} + TEST_F(MemoryClientTest, loadRRSIGFollowsNothing) { EXPECT_THROW(client_->load(Name("example.org"), TEST_DATA_DIR diff --git a/src/lib/datasrc/memory/tests/testdata/Makefile.am b/src/lib/datasrc/memory/tests/testdata/Makefile.am index a674a8b887..ad810b0e05 100644 --- a/src/lib/datasrc/memory/tests/testdata/Makefile.am +++ b/src/lib/datasrc/memory/tests/testdata/Makefile.am @@ -19,3 +19,4 @@ EXTRA_DIST += example.org-nsec3-signed.zone EXTRA_DIST += example.org-nsec3-signed-no-param.zone EXTRA_DIST += example.org-out-of-zone.zone EXTRA_DIST += example.org-wildcard-ns.zone +EXTRA_DIST += example.org-wildcard-dname.zone diff --git a/src/lib/datasrc/memory/tests/testdata/example.org-wildcard-dname.zone b/src/lib/datasrc/memory/tests/testdata/example.org-wildcard-dname.zone new file mode 100644 index 0000000000..0d03b0d5ae --- /dev/null +++ b/src/lib/datasrc/memory/tests/testdata/example.org-wildcard-dname.zone @@ -0,0 +1,4 @@ +;; test zone file with wildcard DNAME names + +example.org. 3600 IN SOA ns1.example.org. bugs.x.w.example.org. 79 3600 300 3600000 3600 +*.example.org. 3600 IN DNAME dname.example.com. From 46286a6732d6adf6f1640ad4ad6b7360a279fea4 Mon Sep 17 00:00:00 2001 From: Mukund Sivaraman Date: Mon, 10 Sep 2012 07:14:04 +0530 Subject: [PATCH 121/199] [2108] Fix RRType in last commit comment --- src/lib/datasrc/memory/tests/memory_client_unittest.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib/datasrc/memory/tests/memory_client_unittest.cc b/src/lib/datasrc/memory/tests/memory_client_unittest.cc index 64214d978d..ff4a0a7bc4 100644 --- a/src/lib/datasrc/memory/tests/memory_client_unittest.cc +++ b/src/lib/datasrc/memory/tests/memory_client_unittest.cc @@ -300,7 +300,7 @@ TEST_F(MemoryClientTest, loadWildcardNSThrows) { } TEST_F(MemoryClientTest, loadWildcardDNAMEThrows) { - // Wildcard NS names should throw + // Wildcard DNAME names should throw EXPECT_THROW(client_->load(Name("example.org"), TEST_DATA_DIR "/example.org-wildcard-dname.zone"), From 2d47ae95847e1088b1458f7d099093d2f5c8d9ef Mon Sep 17 00:00:00 2001 From: Mukund Sivaraman Date: Mon, 10 Sep 2012 07:17:22 +0530 Subject: [PATCH 122/199] [2108] Check that loading a zone with wildcard NSEC3 names throws an exception --- src/lib/datasrc/memory/tests/memory_client_unittest.cc | 9 +++++++++ src/lib/datasrc/memory/tests/testdata/Makefile.am | 1 + .../tests/testdata/example.org-wildcard-nsec3.zone | 4 ++++ 3 files changed, 14 insertions(+) create mode 100644 src/lib/datasrc/memory/tests/testdata/example.org-wildcard-nsec3.zone diff --git a/src/lib/datasrc/memory/tests/memory_client_unittest.cc b/src/lib/datasrc/memory/tests/memory_client_unittest.cc index ff4a0a7bc4..2bcb3f7706 100644 --- a/src/lib/datasrc/memory/tests/memory_client_unittest.cc +++ b/src/lib/datasrc/memory/tests/memory_client_unittest.cc @@ -308,6 +308,15 @@ TEST_F(MemoryClientTest, loadWildcardDNAMEThrows) { // Teardown checks for memory segment leaks } +TEST_F(MemoryClientTest, loadWildcardNSEC3Throws) { + // Wildcard NSEC3 names should throw + EXPECT_THROW(client_->load(Name("example.org"), + TEST_DATA_DIR + "/example.org-wildcard-nsec3.zone"), + InMemoryClient::AddError); + // Teardown checks for memory segment leaks +} + TEST_F(MemoryClientTest, loadRRSIGFollowsNothing) { EXPECT_THROW(client_->load(Name("example.org"), TEST_DATA_DIR diff --git a/src/lib/datasrc/memory/tests/testdata/Makefile.am b/src/lib/datasrc/memory/tests/testdata/Makefile.am index ad810b0e05..02a23e1eb7 100644 --- a/src/lib/datasrc/memory/tests/testdata/Makefile.am +++ b/src/lib/datasrc/memory/tests/testdata/Makefile.am @@ -20,3 +20,4 @@ EXTRA_DIST += example.org-nsec3-signed-no-param.zone EXTRA_DIST += example.org-out-of-zone.zone EXTRA_DIST += example.org-wildcard-ns.zone EXTRA_DIST += example.org-wildcard-dname.zone +EXTRA_DIST += example.org-wildcard-nsec3.zone diff --git a/src/lib/datasrc/memory/tests/testdata/example.org-wildcard-nsec3.zone b/src/lib/datasrc/memory/tests/testdata/example.org-wildcard-nsec3.zone new file mode 100644 index 0000000000..feee116036 --- /dev/null +++ b/src/lib/datasrc/memory/tests/testdata/example.org-wildcard-nsec3.zone @@ -0,0 +1,4 @@ +;; test zone file with wildcard NS names + +example.org. 3600 IN SOA ns1.example.org. bugs.x.w.example.org. 79 3600 300 3600000 3600 +*.example.org. 1200 IN NSEC3 1 0 10 AABBCCDD RKOF8QMFRB5F2V9EJHFBVB2JPVSA0DJD A RRSIG From 41eff487aee5315bdb63b805d1e3e9efe13118f4 Mon Sep 17 00:00:00 2001 From: Mukund Sivaraman Date: Mon, 10 Sep 2012 07:28:37 +0530 Subject: [PATCH 123/199] [2108] Check that loading a zone containing NSEC3 names with invalid number of labels throws --- .../memory/tests/memory_client_unittest.cc | 18 ++++++++++++++++++ .../datasrc/memory/tests/testdata/Makefile.am | 2 ++ .../example.org-nsec3-fewer-labels.zone | 3 +++ .../example.org-nsec3-more-labels.zone | 3 +++ 4 files changed, 26 insertions(+) create mode 100644 src/lib/datasrc/memory/tests/testdata/example.org-nsec3-fewer-labels.zone create mode 100644 src/lib/datasrc/memory/tests/testdata/example.org-nsec3-more-labels.zone diff --git a/src/lib/datasrc/memory/tests/memory_client_unittest.cc b/src/lib/datasrc/memory/tests/memory_client_unittest.cc index 2bcb3f7706..032b9654a4 100644 --- a/src/lib/datasrc/memory/tests/memory_client_unittest.cc +++ b/src/lib/datasrc/memory/tests/memory_client_unittest.cc @@ -317,6 +317,24 @@ TEST_F(MemoryClientTest, loadWildcardNSEC3Throws) { // Teardown checks for memory segment leaks } +TEST_F(MemoryClientTest, loadNSEC3WithFewerLabelsThrows) { + // NSEC3 names with labels != (origin_labels + 1) should throw + EXPECT_THROW(client_->load(Name("example.org"), + TEST_DATA_DIR + "/example.org-nsec3-fewer-labels.zone"), + InMemoryClient::AddError); + // Teardown checks for memory segment leaks +} + +TEST_F(MemoryClientTest, loadNSEC3WithMoreLabelsThrows) { + // NSEC3 names with labels != (origin_labels + 1) should throw + EXPECT_THROW(client_->load(Name("example.org"), + TEST_DATA_DIR + "/example.org-nsec3-more-labels.zone"), + InMemoryClient::AddError); + // Teardown checks for memory segment leaks +} + TEST_F(MemoryClientTest, loadRRSIGFollowsNothing) { EXPECT_THROW(client_->load(Name("example.org"), TEST_DATA_DIR diff --git a/src/lib/datasrc/memory/tests/testdata/Makefile.am b/src/lib/datasrc/memory/tests/testdata/Makefile.am index 02a23e1eb7..721448a416 100644 --- a/src/lib/datasrc/memory/tests/testdata/Makefile.am +++ b/src/lib/datasrc/memory/tests/testdata/Makefile.am @@ -21,3 +21,5 @@ EXTRA_DIST += example.org-out-of-zone.zone EXTRA_DIST += example.org-wildcard-ns.zone EXTRA_DIST += example.org-wildcard-dname.zone EXTRA_DIST += example.org-wildcard-nsec3.zone +EXTRA_DIST += example.org-nsec3-fewer-labels.zone +EXTRA_DIST += example.org-nsec3-more-labels.zone diff --git a/src/lib/datasrc/memory/tests/testdata/example.org-nsec3-fewer-labels.zone b/src/lib/datasrc/memory/tests/testdata/example.org-nsec3-fewer-labels.zone new file mode 100644 index 0000000000..0221269a9b --- /dev/null +++ b/src/lib/datasrc/memory/tests/testdata/example.org-nsec3-fewer-labels.zone @@ -0,0 +1,3 @@ +;; NSEC3 names with labels != (origin_labels + 1) +example.org. 86400 IN SOA ns.example.org. ns.example.org. 2012091001 7200 3600 2592000 1200 +example.org. 1200 IN NSEC3 1 0 10 AABBCCDD RKOF8QMFRB5F2V9EJHFBVB2JPVSA0DJD A RRSIG diff --git a/src/lib/datasrc/memory/tests/testdata/example.org-nsec3-more-labels.zone b/src/lib/datasrc/memory/tests/testdata/example.org-nsec3-more-labels.zone new file mode 100644 index 0000000000..efebcfbf6d --- /dev/null +++ b/src/lib/datasrc/memory/tests/testdata/example.org-nsec3-more-labels.zone @@ -0,0 +1,3 @@ +;; NSEC3 names with labels != (origin_labels + 1) +example.org. 86400 IN SOA ns.example.org. ns.example.org. 2012091002 7200 3600 2592000 1200 +a.b.example.org. 1200 IN NSEC3 1 0 10 AABBCCDD RKOF8QMFRB5F2V9EJHFBVB2JPVSA0DJD A RRSIG From 3f0eb8fec78587398baa9964c344330b77a37f9e Mon Sep 17 00:00:00 2001 From: Mukund Sivaraman Date: Mon, 10 Sep 2012 09:36:53 +0530 Subject: [PATCH 124/199] [2108] If there's an exception, clear last_rrset_ --- src/lib/datasrc/memory/memory_client.cc | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/src/lib/datasrc/memory/memory_client.cc b/src/lib/datasrc/memory/memory_client.cc index fb2a8e2e30..57138eedd3 100644 --- a/src/lib/datasrc/memory/memory_client.cc +++ b/src/lib/datasrc/memory/memory_client.cc @@ -548,11 +548,16 @@ InMemoryClient::InMemoryClientImpl::load( assert(!last_rrset_); - rrset_installer(boost::bind(&InMemoryClientImpl::addFromLoad, this, - _1, zone_name, holder.get())); - - // Add any last RRset that was left - addRdataSet(zone_name, *holder.get(), ConstRRsetPtr(), ConstRRsetPtr()); + try { + rrset_installer(boost::bind(&InMemoryClientImpl::addFromLoad, this, + _1, zone_name, holder.get())); + // Add any last RRset that was left + addRdataSet(zone_name, *holder.get(), + ConstRRsetPtr(), ConstRRsetPtr()); + } catch (...) { + last_rrset_ = ConstRRsetPtr(); + throw; + } assert(!last_rrset_); From 083e6836264daff5da8aa95dff7927d0f37f0e43 Mon Sep 17 00:00:00 2001 From: Mukund Sivaraman Date: Mon, 10 Sep 2012 09:37:26 +0530 Subject: [PATCH 125/199] [2108] Fix context check when adding a CNAME record --- src/lib/datasrc/memory/memory_client.cc | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/src/lib/datasrc/memory/memory_client.cc b/src/lib/datasrc/memory/memory_client.cc index 57138eedd3..e21fd48418 100644 --- a/src/lib/datasrc/memory/memory_client.cc +++ b/src/lib/datasrc/memory/memory_client.cc @@ -178,11 +178,14 @@ public: // owner name except with NSEC, which is the only RR that can coexist // with CNAME (and also RRSIG, which is handled separately) if (rrset.getType() == RRType::CNAME()) { - if (RdataSet::find(set, RRType::NSEC()) != NULL) { - LOG_ERROR(logger, DATASRC_MEM_CNAME_TO_NONEMPTY). - arg(rrset.getName()); - isc_throw(AddError, "CNAME can't be added with other data for " - << rrset.getName()); + for (const RdataSet* sp = set; sp != NULL; sp = sp->getNext()) { + if (sp->type != RRType::NSEC()) { + LOG_ERROR(logger, DATASRC_MEM_CNAME_TO_NONEMPTY). + arg(rrset.getName()); + isc_throw(AddError, "CNAME can't be added with " + << sp->type << " RRType for " + << rrset.getName()); + } } } else if ((rrset.getType() != RRType::NSEC()) && (RdataSet::find(set, RRType::CNAME()) != NULL)) { From e898d07a4540cc9783779f0f39ddcf8264419068 Mon Sep 17 00:00:00 2001 From: Mukund Sivaraman Date: Mon, 10 Sep 2012 09:37:55 +0530 Subject: [PATCH 126/199] [2108] Add CNAME+other tests (which should throw) --- .../memory/tests/memory_client_unittest.cc | 15 +++++++++++++++ src/lib/datasrc/memory/tests/testdata/Makefile.am | 5 +++-- .../example.org-cname-and-not-nsec-1.zone | 4 ++++ .../example.org-cname-and-not-nsec-2.zone | 4 ++++ 4 files changed, 26 insertions(+), 2 deletions(-) create mode 100644 src/lib/datasrc/memory/tests/testdata/example.org-cname-and-not-nsec-1.zone create mode 100644 src/lib/datasrc/memory/tests/testdata/example.org-cname-and-not-nsec-2.zone diff --git a/src/lib/datasrc/memory/tests/memory_client_unittest.cc b/src/lib/datasrc/memory/tests/memory_client_unittest.cc index 032b9654a4..e16431bd1e 100644 --- a/src/lib/datasrc/memory/tests/memory_client_unittest.cc +++ b/src/lib/datasrc/memory/tests/memory_client_unittest.cc @@ -335,6 +335,21 @@ TEST_F(MemoryClientTest, loadNSEC3WithMoreLabelsThrows) { // Teardown checks for memory segment leaks } +TEST_F(MemoryClientTest, loadCNAMEAndNotNSECThrows) { + // CNAME and not NSEC should throw + EXPECT_THROW(client_->load(Name("example.org"), + TEST_DATA_DIR + "/example.org-cname-and-not-nsec-1.zone"), + InMemoryClient::AddError); + + EXPECT_THROW(client_->load(Name("example.org"), + TEST_DATA_DIR + "/example.org-cname-and-not-nsec-2.zone"), + InMemoryClient::AddError); + + // Teardown checks for memory segment leaks +} + TEST_F(MemoryClientTest, loadRRSIGFollowsNothing) { EXPECT_THROW(client_->load(Name("example.org"), TEST_DATA_DIR diff --git a/src/lib/datasrc/memory/tests/testdata/Makefile.am b/src/lib/datasrc/memory/tests/testdata/Makefile.am index 721448a416..23429a4e3b 100644 --- a/src/lib/datasrc/memory/tests/testdata/Makefile.am +++ b/src/lib/datasrc/memory/tests/testdata/Makefile.am @@ -21,5 +21,6 @@ EXTRA_DIST += example.org-out-of-zone.zone EXTRA_DIST += example.org-wildcard-ns.zone EXTRA_DIST += example.org-wildcard-dname.zone EXTRA_DIST += example.org-wildcard-nsec3.zone -EXTRA_DIST += example.org-nsec3-fewer-labels.zone -EXTRA_DIST += example.org-nsec3-more-labels.zone +EXTRA_DIST += example.org-nsec3-fewer-labels.zone example.org-nsec3-more-labels.zone +EXTRA_DIST += example.org-cname-and-not-nsec-1.zone +EXTRA_DIST += example.org-cname-and-not-nsec-2.zone diff --git a/src/lib/datasrc/memory/tests/testdata/example.org-cname-and-not-nsec-1.zone b/src/lib/datasrc/memory/tests/testdata/example.org-cname-and-not-nsec-1.zone new file mode 100644 index 0000000000..d44762d785 --- /dev/null +++ b/src/lib/datasrc/memory/tests/testdata/example.org-cname-and-not-nsec-1.zone @@ -0,0 +1,4 @@ +;; NSEC3 names with labels != (origin_labels + 1) +example.org. 86400 IN SOA ns.example.org. ns.example.org. 2012091009 7200 3600 2592000 1200 +a.example.org. 7200 IN A 192.168.0.1 +a.example.org. 3600 IN CNAME foo.example.com. diff --git a/src/lib/datasrc/memory/tests/testdata/example.org-cname-and-not-nsec-2.zone b/src/lib/datasrc/memory/tests/testdata/example.org-cname-and-not-nsec-2.zone new file mode 100644 index 0000000000..bca72cb499 --- /dev/null +++ b/src/lib/datasrc/memory/tests/testdata/example.org-cname-and-not-nsec-2.zone @@ -0,0 +1,4 @@ +;; NSEC3 names with labels != (origin_labels + 1) +example.org. 86400 IN SOA ns.example.org. ns.example.org. 2012091007 7200 3600 2592000 1200 +a.example.org. 3600 IN CNAME foo.example.com. +a.example.org. 7200 IN A 192.168.0.1 From f4ceb192ddc140409230eef5ecc55a13f04059f5 Mon Sep 17 00:00:00 2001 From: Mukund Sivaraman Date: Mon, 10 Sep 2012 09:41:50 +0530 Subject: [PATCH 127/199] [2108] Fix zone file comments --- .../memory/tests/testdata/example.org-cname-and-not-nsec-1.zone | 2 +- .../memory/tests/testdata/example.org-cname-and-not-nsec-2.zone | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/lib/datasrc/memory/tests/testdata/example.org-cname-and-not-nsec-1.zone b/src/lib/datasrc/memory/tests/testdata/example.org-cname-and-not-nsec-1.zone index d44762d785..5533663f69 100644 --- a/src/lib/datasrc/memory/tests/testdata/example.org-cname-and-not-nsec-1.zone +++ b/src/lib/datasrc/memory/tests/testdata/example.org-cname-and-not-nsec-1.zone @@ -1,4 +1,4 @@ -;; NSEC3 names with labels != (origin_labels + 1) +;; CNAME + other is an error example.org. 86400 IN SOA ns.example.org. ns.example.org. 2012091009 7200 3600 2592000 1200 a.example.org. 7200 IN A 192.168.0.1 a.example.org. 3600 IN CNAME foo.example.com. diff --git a/src/lib/datasrc/memory/tests/testdata/example.org-cname-and-not-nsec-2.zone b/src/lib/datasrc/memory/tests/testdata/example.org-cname-and-not-nsec-2.zone index bca72cb499..966aeeb9a5 100644 --- a/src/lib/datasrc/memory/tests/testdata/example.org-cname-and-not-nsec-2.zone +++ b/src/lib/datasrc/memory/tests/testdata/example.org-cname-and-not-nsec-2.zone @@ -1,4 +1,4 @@ -;; NSEC3 names with labels != (origin_labels + 1) +;; CNAME + other is an error example.org. 86400 IN SOA ns.example.org. ns.example.org. 2012091007 7200 3600 2592000 1200 a.example.org. 3600 IN CNAME foo.example.com. a.example.org. 7200 IN A 192.168.0.1 From a376d4a632d9510ba8f4175b4221b245b812d65b Mon Sep 17 00:00:00 2001 From: Mukund Sivaraman Date: Mon, 10 Sep 2012 09:55:26 +0530 Subject: [PATCH 128/199] [2108] Add DNAME+NS tests --- .../memory/tests/memory_client_unittest.cc | 34 +++++++++++++++++++ .../datasrc/memory/tests/testdata/Makefile.am | 4 +++ .../testdata/example.org-dname-ns-apex-1.zone | 4 +++ .../testdata/example.org-dname-ns-apex-2.zone | 4 +++ .../example.org-dname-ns-nonapex-1.zone | 4 +++ .../example.org-dname-ns-nonapex-2.zone | 4 +++ 6 files changed, 54 insertions(+) create mode 100644 src/lib/datasrc/memory/tests/testdata/example.org-dname-ns-apex-1.zone create mode 100644 src/lib/datasrc/memory/tests/testdata/example.org-dname-ns-apex-2.zone create mode 100644 src/lib/datasrc/memory/tests/testdata/example.org-dname-ns-nonapex-1.zone create mode 100644 src/lib/datasrc/memory/tests/testdata/example.org-dname-ns-nonapex-2.zone diff --git a/src/lib/datasrc/memory/tests/memory_client_unittest.cc b/src/lib/datasrc/memory/tests/memory_client_unittest.cc index e16431bd1e..771f036d07 100644 --- a/src/lib/datasrc/memory/tests/memory_client_unittest.cc +++ b/src/lib/datasrc/memory/tests/memory_client_unittest.cc @@ -350,6 +350,40 @@ TEST_F(MemoryClientTest, loadCNAMEAndNotNSECThrows) { // Teardown checks for memory segment leaks } +TEST_F(MemoryClientTest, loadDNAMEAndNSApex1) { + // DNAME + NS (apex) is OK + client_->load(Name("example.org"), + TEST_DATA_DIR + "/example.org-dname-ns-apex-1.zone"); + // Teardown checks for memory segment leaks +} + +TEST_F(MemoryClientTest, loadDNAMEAndNSApex2) { + // DNAME + NS (apex) is OK (reverse order) + client_->load(Name("example.org"), + TEST_DATA_DIR + "/example.org-dname-ns-apex-2.zone"); + // Teardown checks for memory segment leaks +} + +TEST_F(MemoryClientTest, loadDNAMEAndNSNonApex1) { + // DNAME + NS (non-apex) must throw + EXPECT_THROW(client_->load(Name("example.org"), + TEST_DATA_DIR + "/example.org-dname-ns-nonapex-1.zone"), + InMemoryClient::AddError); + // Teardown checks for memory segment leaks +} + +TEST_F(MemoryClientTest, loadDNAMEAndNSNonApex2) { + // DNAME + NS (non-apex) must throw (reverse order) + EXPECT_THROW(client_->load(Name("example.org"), + TEST_DATA_DIR + "/example.org-dname-ns-nonapex-2.zone"), + InMemoryClient::AddError); + // Teardown checks for memory segment leaks +} + TEST_F(MemoryClientTest, loadRRSIGFollowsNothing) { EXPECT_THROW(client_->load(Name("example.org"), TEST_DATA_DIR diff --git a/src/lib/datasrc/memory/tests/testdata/Makefile.am b/src/lib/datasrc/memory/tests/testdata/Makefile.am index 23429a4e3b..8e0c2c8583 100644 --- a/src/lib/datasrc/memory/tests/testdata/Makefile.am +++ b/src/lib/datasrc/memory/tests/testdata/Makefile.am @@ -24,3 +24,7 @@ EXTRA_DIST += example.org-wildcard-nsec3.zone EXTRA_DIST += example.org-nsec3-fewer-labels.zone example.org-nsec3-more-labels.zone EXTRA_DIST += example.org-cname-and-not-nsec-1.zone EXTRA_DIST += example.org-cname-and-not-nsec-2.zone +EXTRA_DIST += example.org-dname-ns-nonapex-1.zone +EXTRA_DIST += example.org-dname-ns-nonapex-2.zone +EXTRA_DIST += example.org-dname-ns-apex-1.zone +EXTRA_DIST += example.org-dname-ns-apex-2.zone diff --git a/src/lib/datasrc/memory/tests/testdata/example.org-dname-ns-apex-1.zone b/src/lib/datasrc/memory/tests/testdata/example.org-dname-ns-apex-1.zone new file mode 100644 index 0000000000..f57c25d3f2 --- /dev/null +++ b/src/lib/datasrc/memory/tests/testdata/example.org-dname-ns-apex-1.zone @@ -0,0 +1,4 @@ +;; DNAME + NS (apex) +example.org. 86400 IN SOA ns.example.org. ns.example.org. 2012091015 7200 3600 2592000 1200 +example.org. 3600 IN DNAME foo.example.com. +example.org. 3600 IN NS bar.example.com. diff --git a/src/lib/datasrc/memory/tests/testdata/example.org-dname-ns-apex-2.zone b/src/lib/datasrc/memory/tests/testdata/example.org-dname-ns-apex-2.zone new file mode 100644 index 0000000000..bb3f1918d7 --- /dev/null +++ b/src/lib/datasrc/memory/tests/testdata/example.org-dname-ns-apex-2.zone @@ -0,0 +1,4 @@ +;; DNAME + NS (apex) +example.org. 86400 IN SOA ns.example.org. ns.example.org. 2012091016 7200 3600 2592000 1200 +example.org. 3600 IN NS bar.example.com. +example.org. 3600 IN DNAME foo.example.com. diff --git a/src/lib/datasrc/memory/tests/testdata/example.org-dname-ns-nonapex-1.zone b/src/lib/datasrc/memory/tests/testdata/example.org-dname-ns-nonapex-1.zone new file mode 100644 index 0000000000..68a980553d --- /dev/null +++ b/src/lib/datasrc/memory/tests/testdata/example.org-dname-ns-nonapex-1.zone @@ -0,0 +1,4 @@ +;; DNAME + NS (non-apex) +example.org. 86400 IN SOA ns.example.org. ns.example.org. 2012091014 7200 3600 2592000 1200 +ns1.example.org. 3600 IN DNAME foo.example.com. +ns1.example.org. 3600 IN NS bar.example.com. diff --git a/src/lib/datasrc/memory/tests/testdata/example.org-dname-ns-nonapex-2.zone b/src/lib/datasrc/memory/tests/testdata/example.org-dname-ns-nonapex-2.zone new file mode 100644 index 0000000000..1b671dd1d9 --- /dev/null +++ b/src/lib/datasrc/memory/tests/testdata/example.org-dname-ns-nonapex-2.zone @@ -0,0 +1,4 @@ +;; DNAME + NS (non-apex) +example.org. 86400 IN SOA ns.example.org. ns.example.org. 2012091015 7200 3600 2592000 1200 +ns1.example.org. 3600 IN NS bar.example.com. +ns1.example.org. 3600 IN DNAME foo.example.com. From 72f8b8a0db78bab56f8efb0f344fc8961f9adadc Mon Sep 17 00:00:00 2001 From: Mukund Sivaraman Date: Mon, 10 Sep 2012 10:47:54 +0530 Subject: [PATCH 129/199] [2108] getTTL() is unimplemented in TreeNodeRRset For now, let's use RRTTL(0) and revisit it. --- src/lib/datasrc/memory/memory_client.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib/datasrc/memory/memory_client.cc b/src/lib/datasrc/memory/memory_client.cc index e21fd48418..12e68df463 100644 --- a/src/lib/datasrc/memory/memory_client.cc +++ b/src/lib/datasrc/memory/memory_client.cc @@ -839,7 +839,7 @@ public: RRsetPtr result(new RRset(rrset_->getName(), rrset_->getClass(), rrset_->getType(), - rrset_->getTTL())); + RRTTL(0))); result->addRdata(rdata_iterator_->getCurrent()); rdata_iterator_->next(); if (rdata_iterator_->isLast()) { From c531154142d7466163731429af8050ede78bc423 Mon Sep 17 00:00:00 2001 From: Mukund Sivaraman Date: Mon, 10 Sep 2012 10:48:04 +0530 Subject: [PATCH 130/199] [2108] Fix comment --- src/lib/datasrc/memory/tests/memory_client_unittest.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib/datasrc/memory/tests/memory_client_unittest.cc b/src/lib/datasrc/memory/tests/memory_client_unittest.cc index 771f036d07..18774be651 100644 --- a/src/lib/datasrc/memory/tests/memory_client_unittest.cc +++ b/src/lib/datasrc/memory/tests/memory_client_unittest.cc @@ -468,7 +468,7 @@ TEST_F(MemoryClientTest, getIterator) { EXPECT_TRUE(rrset_soa); EXPECT_EQ(RRType::SOA(), rrset_soa->getType()); - // There's nothing else in this zone + // There's nothing else in this iterator EXPECT_EQ(ConstRRsetPtr(), iterator->getNextRRset()); // Iterating past the end should result in an exception From f60ea8d945a1c503670418c7e3f6cc57e5b82ee1 Mon Sep 17 00:00:00 2001 From: Mukund Sivaraman Date: Mon, 10 Sep 2012 10:48:20 +0530 Subject: [PATCH 131/199] [2108] Test iterator with separate_rrs=true --- .../memory/tests/memory_client_unittest.cc | 43 +++++++++++++++++++ .../datasrc/memory/tests/testdata/Makefile.am | 1 + .../tests/testdata/example.org-multiple.zone | 4 ++ 3 files changed, 48 insertions(+) create mode 100644 src/lib/datasrc/memory/tests/testdata/example.org-multiple.zone diff --git a/src/lib/datasrc/memory/tests/memory_client_unittest.cc b/src/lib/datasrc/memory/tests/memory_client_unittest.cc index 18774be651..ee747a31cc 100644 --- a/src/lib/datasrc/memory/tests/memory_client_unittest.cc +++ b/src/lib/datasrc/memory/tests/memory_client_unittest.cc @@ -475,6 +475,49 @@ TEST_F(MemoryClientTest, getIterator) { EXPECT_THROW(iterator->getNextRRset(), isc::Unexpected); } +TEST_F(MemoryClientTest, getIteratorSeparateRRs) { + client_->load(Name("example.org"), + TEST_DATA_DIR "/example.org-multiple.zone"); + + // separate_rrs = false + ZoneIteratorPtr iterator(client_->getIterator(Name("example.org"))); + + // First we have the SOA + ConstRRsetPtr rrset(iterator->getNextRRset()); + EXPECT_TRUE(rrset); + EXPECT_EQ(RRType::SOA(), rrset->getType()); + + // Only one RRType::A() RRset + rrset = iterator->getNextRRset(); + EXPECT_TRUE(rrset); + EXPECT_EQ(RRType::A(), rrset->getType()); + + // There's nothing else in this zone + EXPECT_EQ(ConstRRsetPtr(), iterator->getNextRRset()); + + + // separate_rrs = true + ZoneIteratorPtr iterator2(client_->getIterator(Name("example.org"), true)); + + // First we have the SOA + rrset = iterator2->getNextRRset(); + EXPECT_TRUE(rrset); + EXPECT_EQ(RRType::SOA(), rrset->getType()); + + // First RRType::A() RRset + rrset = iterator2->getNextRRset(); + EXPECT_TRUE(rrset); + EXPECT_EQ(RRType::A(), rrset->getType()); + + // Second RRType::A() RRset + rrset = iterator2->getNextRRset(); + EXPECT_TRUE(rrset); + EXPECT_EQ(RRType::A(), rrset->getType()); + + // There's nothing else in this iterator + EXPECT_EQ(ConstRRsetPtr(), iterator2->getNextRRset()); +} + TEST_F(MemoryClientTest, getIteratorGetSOAThrowsNotImplemented) { client_->load(Name("example.org"), TEST_DATA_DIR "/example.org-empty.zone"); ZoneIteratorPtr iterator(client_->getIterator(Name("example.org"))); diff --git a/src/lib/datasrc/memory/tests/testdata/Makefile.am b/src/lib/datasrc/memory/tests/testdata/Makefile.am index 8e0c2c8583..17fb97a964 100644 --- a/src/lib/datasrc/memory/tests/testdata/Makefile.am +++ b/src/lib/datasrc/memory/tests/testdata/Makefile.am @@ -3,6 +3,7 @@ CLEANFILES = *.copied EXTRA_DIST = empty.zone EXTRA_DIST += example.org.zone EXTRA_DIST += example.org-empty.zone +EXTRA_DIST += example.org-multiple.zone EXTRA_DIST += example.org-broken1.zone EXTRA_DIST += example.org-broken2.zone EXTRA_DIST += example.org-rrsig-follows-nothing.zone diff --git a/src/lib/datasrc/memory/tests/testdata/example.org-multiple.zone b/src/lib/datasrc/memory/tests/testdata/example.org-multiple.zone new file mode 100644 index 0000000000..f473ae6e0c --- /dev/null +++ b/src/lib/datasrc/memory/tests/testdata/example.org-multiple.zone @@ -0,0 +1,4 @@ +;; Multiple RDATA for testing separate RRs iterator +example.org. 3600 IN SOA ns1.example.org. bugs.x.w.example.org. 78 3600 300 3600000 3600 +a.example.org. 3600 IN A 192.168.0.1 +a.example.org. 3600 IN A 192.168.0.2 From a727e666876aa6ec02cc9d736b2735c4d021fca4 Mon Sep 17 00:00:00 2001 From: Mukund Sivaraman Date: Mon, 10 Sep 2012 11:06:29 +0530 Subject: [PATCH 132/199] [2108] Implement TreeNodeRRset::getTTL() This is used in the iterator when separating RRs. --- .../memory/tests/treenode_rrset_unittest.cc | 16 +++++++++------- src/lib/datasrc/memory/treenode_rrset.cc | 10 ++++++++-- src/lib/datasrc/memory/treenode_rrset.h | 4 +++- 3 files changed, 20 insertions(+), 10 deletions(-) diff --git a/src/lib/datasrc/memory/tests/treenode_rrset_unittest.cc b/src/lib/datasrc/memory/tests/treenode_rrset_unittest.cc index cf486bf9da..c83c034366 100644 --- a/src/lib/datasrc/memory/tests/treenode_rrset_unittest.cc +++ b/src/lib/datasrc/memory/tests/treenode_rrset_unittest.cc @@ -138,11 +138,13 @@ protected: void checkBasicFields(const AbstractRRset& actual_rrset, const Name& expected_name, const RRClass& expected_class, const RRType& expected_type, + const uint32_t expected_ttl, size_t expected_rdatacount, size_t expected_sigcount) { EXPECT_EQ(expected_name, actual_rrset.getName()); EXPECT_EQ(expected_class, actual_rrset.getClass()); EXPECT_EQ(expected_type, actual_rrset.getType()); + EXPECT_EQ(RRTTL(expected_ttl), actual_rrset.getTTL()); EXPECT_EQ(expected_rdatacount, actual_rrset.getRdataCount()); EXPECT_EQ(expected_sigcount, actual_rrset.getRRsigDataCount()); } @@ -150,31 +152,31 @@ checkBasicFields(const AbstractRRset& actual_rrset, const Name& expected_name, TEST_F(TreeNodeRRsetTest, create) { // Constructed with RRSIG, and it should be visible. checkBasicFields(TreeNodeRRset(rrclass_, www_node_, a_rdataset_, true), - www_name_, rrclass_, RRType::A(), 2, 1); + www_name_, rrclass_, RRType::A(), 3600, 2, 1); // Constructed with RRSIG, and it should be invisible. checkBasicFields(TreeNodeRRset(rrclass_, www_node_, a_rdataset_, false), - www_name_, rrclass_, RRType::A(), 2, 0); + www_name_, rrclass_, RRType::A(), 3600, 2, 0); // Constructed without RRSIG, and it would be visible (but of course won't) checkBasicFields(TreeNodeRRset(rrclass_, origin_node_, ns_rdataset_, true), - origin_name_, rrclass_, RRType::NS(), 1, 0); + origin_name_, rrclass_, RRType::NS(), 3600, 1, 0); // Constructed without RRSIG, and it should be visible checkBasicFields(TreeNodeRRset(rrclass_, origin_node_, ns_rdataset_, false), - origin_name_, rrclass_, RRType::NS(), 1, 0); + origin_name_, rrclass_, RRType::NS(), 3600, 1, 0); // RRSIG-only case (note the RRset's type is covered type) checkBasicFields(TreeNodeRRset(rrclass_, www_node_, rrsig_only_rdataset_, true), - www_name_, rrclass_, RRType::TXT(), 0, 1); + www_name_, rrclass_, RRType::TXT(), 3600, 0, 1); // RRSIG-only case (note the RRset's type is covered type), but it's // invisible checkBasicFields(TreeNodeRRset(rrclass_, www_node_, rrsig_only_rdataset_, false), - www_name_, rrclass_, RRType::TXT(), 0, 0); + www_name_, rrclass_, RRType::TXT(), 3600, 0, 0); // Wildcard substitution checkBasicFields(TreeNodeRRset(match_name_, rrclass_, wildcard_node_, wildcard_rdataset_, true), - match_name_, rrclass_, RRType::A(), 2, 1); + match_name_, rrclass_, RRType::A(), 3600, 2, 1); } // Templated if and when we support OutputBuffer version of toWire(). diff --git a/src/lib/datasrc/memory/treenode_rrset.cc b/src/lib/datasrc/memory/treenode_rrset.cc index 6fc6b66682..1f4bc1d02e 100644 --- a/src/lib/datasrc/memory/treenode_rrset.cc +++ b/src/lib/datasrc/memory/treenode_rrset.cc @@ -46,7 +46,7 @@ TreeNodeRRset::TreeNodeRRset(const dns::Name& realname, bool dnssec_ok) : node_(node), rdataset_(rdataset), rrsig_count_(rdataset_->getSigRdataCount()), rrclass_(rrclass), - dnssec_ok_(dnssec_ok), name_(NULL) + dnssec_ok_(dnssec_ok), name_(NULL), ttl_(NULL) { const LabelSequence labels(realname); const size_t labels_storangelen = labels.getSerializedLength(); @@ -69,7 +69,13 @@ TreeNodeRRset::getName() const { const RRTTL& TreeNodeRRset::getTTL() const { - isc_throw(Unexpected, "unexpected method called on TreeNodeRRset"); + if (ttl_ == NULL) { + util::InputBuffer ttl_buffer(rdataset_->getTTLData(), + sizeof(uint32_t)); + ttl_ = new RRTTL(ttl_buffer); + } + + return (*ttl_); } void diff --git a/src/lib/datasrc/memory/treenode_rrset.h b/src/lib/datasrc/memory/treenode_rrset.h index 1f52124386..91f81e859b 100644 --- a/src/lib/datasrc/memory/treenode_rrset.h +++ b/src/lib/datasrc/memory/treenode_rrset.h @@ -105,7 +105,7 @@ public: const RdataSet* rdataset, bool dnssec_ok) : node_(node), rdataset_(rdataset), rrsig_count_(rdataset_->getSigRdataCount()), rrclass_(rrclass), - dnssec_ok_(dnssec_ok), name_(NULL), realname_buf_(NULL) + dnssec_ok_(dnssec_ok), name_(NULL), realname_buf_(NULL), ttl_(NULL) {} /// \brief Constructor for wildcard-expanded owner name. @@ -126,6 +126,7 @@ public: virtual ~TreeNodeRRset() { delete[] realname_buf_; + delete ttl_; delete name_; } @@ -251,6 +252,7 @@ private: const bool dnssec_ok_; mutable dns::Name* name_; uint8_t* realname_buf_; + mutable dns::RRTTL* ttl_; }; } // namespace memory From 5bd8df4b94383665c2c6de44436b3eeedae7f2d4 Mon Sep 17 00:00:00 2001 From: Mukund Sivaraman Date: Mon, 10 Sep 2012 11:06:44 +0530 Subject: [PATCH 133/199] Revert "[2108] getTTL() is unimplemented in TreeNodeRRset" This reverts commit 72f8b8a0db78bab56f8efb0f344fc8961f9adadc. --- src/lib/datasrc/memory/memory_client.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib/datasrc/memory/memory_client.cc b/src/lib/datasrc/memory/memory_client.cc index 12e68df463..e21fd48418 100644 --- a/src/lib/datasrc/memory/memory_client.cc +++ b/src/lib/datasrc/memory/memory_client.cc @@ -839,7 +839,7 @@ public: RRsetPtr result(new RRset(rrset_->getName(), rrset_->getClass(), rrset_->getType(), - RRTTL(0))); + rrset_->getTTL())); result->addRdata(rdata_iterator_->getCurrent()); rdata_iterator_->next(); if (rdata_iterator_->isLast()) { From eb66b3c19f083d918a536b422376217851896d0f Mon Sep 17 00:00:00 2001 From: Mukund Sivaraman Date: Mon, 10 Sep 2012 11:09:21 +0530 Subject: [PATCH 134/199] [2108] Sort zone files in Makefile.am --- .../datasrc/memory/tests/testdata/Makefile.am | 37 ++++++++++--------- 1 file changed, 19 insertions(+), 18 deletions(-) diff --git a/src/lib/datasrc/memory/tests/testdata/Makefile.am b/src/lib/datasrc/memory/tests/testdata/Makefile.am index 17fb97a964..9fb99866a3 100644 --- a/src/lib/datasrc/memory/tests/testdata/Makefile.am +++ b/src/lib/datasrc/memory/tests/testdata/Makefile.am @@ -3,29 +3,30 @@ CLEANFILES = *.copied EXTRA_DIST = empty.zone EXTRA_DIST += example.org.zone EXTRA_DIST += example.org-empty.zone -EXTRA_DIST += example.org-multiple.zone + EXTRA_DIST += example.org-broken1.zone EXTRA_DIST += example.org-broken2.zone -EXTRA_DIST += example.org-rrsig-follows-nothing.zone -EXTRA_DIST += example.org-rrsig-name-unmatched.zone -EXTRA_DIST += example.org-rrsig-type-unmatched.zone -EXTRA_DIST += example.org-rrsigs.zone -EXTRA_DIST += example.org-duplicate-type.zone +EXTRA_DIST += example.org-cname-and-not-nsec-1.zone +EXTRA_DIST += example.org-cname-and-not-nsec-2.zone +EXTRA_DIST += example.org-dname-ns-apex-1.zone +EXTRA_DIST += example.org-dname-ns-apex-2.zone +EXTRA_DIST += example.org-dname-ns-nonapex-1.zone +EXTRA_DIST += example.org-dname-ns-nonapex-2.zone EXTRA_DIST += example.org-duplicate-type-bad.zone +EXTRA_DIST += example.org-duplicate-type.zone EXTRA_DIST += example.org-multiple-cname.zone EXTRA_DIST += example.org-multiple-dname.zone EXTRA_DIST += example.org-multiple-nsec3.zone EXTRA_DIST += example.org-multiple-nsec3param.zone -EXTRA_DIST += example.org-nsec3-signed.zone -EXTRA_DIST += example.org-nsec3-signed-no-param.zone -EXTRA_DIST += example.org-out-of-zone.zone -EXTRA_DIST += example.org-wildcard-ns.zone -EXTRA_DIST += example.org-wildcard-dname.zone -EXTRA_DIST += example.org-wildcard-nsec3.zone +EXTRA_DIST += example.org-multiple.zone EXTRA_DIST += example.org-nsec3-fewer-labels.zone example.org-nsec3-more-labels.zone -EXTRA_DIST += example.org-cname-and-not-nsec-1.zone -EXTRA_DIST += example.org-cname-and-not-nsec-2.zone -EXTRA_DIST += example.org-dname-ns-nonapex-1.zone -EXTRA_DIST += example.org-dname-ns-nonapex-2.zone -EXTRA_DIST += example.org-dname-ns-apex-1.zone -EXTRA_DIST += example.org-dname-ns-apex-2.zone +EXTRA_DIST += example.org-nsec3-signed-no-param.zone +EXTRA_DIST += example.org-nsec3-signed.zone +EXTRA_DIST += example.org-out-of-zone.zone +EXTRA_DIST += example.org-rrsig-follows-nothing.zone +EXTRA_DIST += example.org-rrsig-name-unmatched.zone +EXTRA_DIST += example.org-rrsig-type-unmatched.zone +EXTRA_DIST += example.org-rrsigs.zone +EXTRA_DIST += example.org-wildcard-dname.zone +EXTRA_DIST += example.org-wildcard-ns.zone +EXTRA_DIST += example.org-wildcard-nsec3.zone From 8716a098a9205bf1aeae4dd61d7c2e4fb4956c47 Mon Sep 17 00:00:00 2001 From: Mukund Sivaraman Date: Mon, 10 Sep 2012 12:08:06 +0530 Subject: [PATCH 135/199] [2108] Add unique prefix to datasrc::memory message IDs --- src/lib/datasrc/memory/memory_client.cc | 28 ++++++++++---------- src/lib/datasrc/memory/memory_messages.mes | 30 +++++++++++----------- 2 files changed, 29 insertions(+), 29 deletions(-) diff --git a/src/lib/datasrc/memory/memory_client.cc b/src/lib/datasrc/memory/memory_client.cc index e21fd48418..32b9d2b128 100644 --- a/src/lib/datasrc/memory/memory_client.cc +++ b/src/lib/datasrc/memory/memory_client.cc @@ -147,7 +147,7 @@ public: l > origin_labels; --l, wname = wname.split(1)) { if (wname.isWildcard()) { - LOG_DEBUG(logger, DBG_TRACE_DATA, DATASRC_MEM_ADD_WILDCARD). + LOG_DEBUG(logger, DBG_TRACE_DATA, DATASRC_MEMORY_MEM_ADD_WILDCARD). arg(name); // Ensure a separate level exists for the "wildcarding" name, @@ -180,7 +180,7 @@ public: if (rrset.getType() == RRType::CNAME()) { for (const RdataSet* sp = set; sp != NULL; sp = sp->getNext()) { if (sp->type != RRType::NSEC()) { - LOG_ERROR(logger, DATASRC_MEM_CNAME_TO_NONEMPTY). + LOG_ERROR(logger, DATASRC_MEMORY_MEM_CNAME_TO_NONEMPTY). arg(rrset.getName()); isc_throw(AddError, "CNAME can't be added with " << sp->type << " RRType for " @@ -189,7 +189,7 @@ public: } } else if ((rrset.getType() != RRType::NSEC()) && (RdataSet::find(set, RRType::CNAME()) != NULL)) { - LOG_ERROR(logger, DATASRC_MEM_CNAME_COEXIST).arg(rrset.getName()); + LOG_ERROR(logger, DATASRC_MEMORY_MEM_CNAME_COEXIST).arg(rrset.getName()); isc_throw(AddError, "CNAME and " << rrset.getType() << " can't coexist for " << rrset.getName()); } @@ -207,7 +207,7 @@ public: (rrset.getType() == RRType::NS() && RdataSet::find(set, RRType::DNAME()) != NULL))) { - LOG_ERROR(logger, DATASRC_MEM_DNAME_NS).arg(rrset.getName()); + LOG_ERROR(logger, DATASRC_MEMORY_MEM_DNAME_NS).arg(rrset.getName()); isc_throw(AddError, "DNAME can't coexist with NS in non-apex " "domain " << rrset.getName()); } @@ -233,7 +233,7 @@ public: // XXX: this is not only for CNAME or DNAME. We should generalize // this code for all other "singleton RR types" (such as SOA) in a // separate task. - LOG_ERROR(logger, DATASRC_MEM_SINGLETON).arg(rrset->getName()). + LOG_ERROR(logger, DATASRC_MEMORY_MEM_SINGLETON).arg(rrset->getName()). arg(rrset->getType()); isc_throw(AddError, "multiple RRs of singleton type for " << rrset->getName()); @@ -251,7 +251,7 @@ public: if (compare.getRelation() != NameComparisonResult::SUPERDOMAIN && compare.getRelation() != NameComparisonResult::EQUAL) { - LOG_ERROR(logger, DATASRC_MEM_OUT_OF_ZONE).arg(rrset->getName()). + LOG_ERROR(logger, DATASRC_MEMORY_MEM_OUT_OF_ZONE).arg(rrset->getName()). arg(zone_name); isc_throw(OutOfZone, "The name " << rrset->getName() << " is not contained in zone " << zone_name); @@ -267,13 +267,13 @@ public: // behavior. if (rrset->getName().isWildcard()) { if (rrset->getType() == RRType::NS()) { - LOG_ERROR(logger, DATASRC_MEM_WILDCARD_NS). + LOG_ERROR(logger, DATASRC_MEMORY_MEM_WILDCARD_NS). arg(rrset->getName()); isc_throw(AddError, "Invalid NS owner name (wildcard): " << rrset->getName()); } if (rrset->getType() == RRType::DNAME()) { - LOG_ERROR(logger, DATASRC_MEM_WILDCARD_DNAME). + LOG_ERROR(logger, DATASRC_MEMORY_MEM_WILDCARD_DNAME). arg(rrset->getName()); isc_throw(AddError, "Invalid DNAME owner name (wildcard): " << rrset->getName()); @@ -289,7 +289,7 @@ public: (rrset->getName().isWildcard() || rrset->getName().getLabelCount() != zone_name.getLabelCount() + 1)) { - LOG_ERROR(logger, DATASRC_BAD_NSEC3_NAME). + LOG_ERROR(logger, DATASRC_MEMORY_BAD_NSEC3_NAME). arg(rrset->getName()); isc_throw(AddError, "Invalid NSEC3 owner name: " << rrset->getName()); @@ -498,7 +498,7 @@ public: addValidation(zone_name, rrset); // OK, can add the RRset. - LOG_DEBUG(logger, DBG_TRACE_DATA, DATASRC_MEM_ADD_RRSET). + LOG_DEBUG(logger, DBG_TRACE_DATA, DATASRC_MEMORY_MEM_ADD_RRSET). arg(rrset->getName()).arg(rrset->getType()).arg(zone_name); if (rrset->getType() == RRType::NSEC3()) { @@ -572,7 +572,7 @@ InMemoryClient::InMemoryClientImpl::load( // process only adds new nodes (and their data), so this assertion // should hold. if (RdataSet::find(set, RRType::NSEC3PARAM()) == NULL) { - LOG_WARN(logger, DATASRC_MEM_NO_NSEC3PARAM). + LOG_WARN(logger, DATASRC_MEMORY_MEM_NO_NSEC3PARAM). arg(zone_name).arg(rrclass_); } } @@ -585,7 +585,7 @@ InMemoryClient::InMemoryClientImpl::load( "Won't create an empty zone for: " << zone_name); } - LOG_DEBUG(logger, DBG_TRACE_BASIC, DATASRC_MEM_ADD_ZONE). + LOG_DEBUG(logger, DBG_TRACE_BASIC, DATASRC_MEMORY_MEM_ADD_ZONE). arg(zone_name).arg(rrclass_.toText()); // Set the filename in file_name_tree_ now, so that getFileName() @@ -688,7 +688,7 @@ InMemoryClient::getZoneCount() const { isc::datasrc::memory::ZoneTable::FindResult InMemoryClient::findZone2(const isc::dns::Name& zone_name) const { - LOG_DEBUG(logger, DBG_TRACE_DATA, DATASRC_MEM_FIND_ZONE).arg(zone_name); + LOG_DEBUG(logger, DBG_TRACE_DATA, DATASRC_MEMORY_MEM_FIND_ZONE).arg(zone_name); ZoneTable::FindResult result(impl_->zone_table_->findZone(zone_name)); return (result); } @@ -705,7 +705,7 @@ InMemoryClient::findZone(const isc::dns::Name&) const { result::Result InMemoryClient::load(const isc::dns::Name& zone_name, const std::string& filename) { - LOG_DEBUG(logger, DBG_TRACE_BASIC, DATASRC_MEM_LOAD).arg(zone_name). + LOG_DEBUG(logger, DBG_TRACE_BASIC, DATASRC_MEMORY_MEM_LOAD).arg(zone_name). arg(filename); return (impl_->load(zone_name, filename, diff --git a/src/lib/datasrc/memory/memory_messages.mes b/src/lib/datasrc/memory/memory_messages.mes index 6fe63c79c3..a66297c334 100644 --- a/src/lib/datasrc/memory/memory_messages.mes +++ b/src/lib/datasrc/memory/memory_messages.mes @@ -16,62 +16,62 @@ $NAMESPACE isc::datasrc::memory # \brief Messages for the data source memory library -% DATASRC_MEM_ADD_WILDCARD adding wildcards for '%1' +% DATASRC_MEMORY_MEM_ADD_WILDCARD adding wildcards for '%1' This is a debug message issued during the processing of a wildcard name. The internal domain name tree is scanned and some nodes are specially marked to allow the wildcard lookup to succeed. -% DATASRC_MEM_CNAME_TO_NONEMPTY can't add CNAME to domain with other data in '%1' +% DATASRC_MEMORY_MEM_CNAME_TO_NONEMPTY can't add CNAME to domain with other data in '%1' Someone or something tried to add a CNAME into a domain that already contains some other data. But the protocol forbids coexistence of CNAME with anything (RFC 1034, section 3.6.2). This indicates a problem with provided data. -% DATASRC_MEM_CNAME_COEXIST can't add data to CNAME in domain '%1' +% DATASRC_MEMORY_MEM_CNAME_COEXIST can't add data to CNAME in domain '%1' This is the same problem as in MEM_CNAME_TO_NONEMPTY, but it happened the other way around -- adding some other data to CNAME. -% DATASRC_MEM_DNAME_NS DNAME and NS can't coexist in non-apex domain '%1' +% DATASRC_MEMORY_MEM_DNAME_NS DNAME and NS can't coexist in non-apex domain '%1' A request was made for DNAME and NS records to be put into the same domain which is not the apex (the top of the zone). This is forbidden by RFC 2672 (section 3) and indicates a problem with provided data. -% DATASRC_MEM_SINGLETON trying to add multiple RRs for domain '%1' and type '%2' +% DATASRC_MEMORY_MEM_SINGLETON trying to add multiple RRs for domain '%1' and type '%2' Some resource types are singletons -- only one is allowed in a domain (for example CNAME or SOA). This indicates a problem with provided data. -% DATASRC_MEM_OUT_OF_ZONE domain '%1' doesn't belong to zone '%2' +% DATASRC_MEMORY_MEM_OUT_OF_ZONE domain '%1' doesn't belong to zone '%2' It was attempted to add the domain into a zone that shouldn't have it (eg. the domain is not subdomain of the zone origin). This indicates a problem with provided data. -% DATASRC_MEM_WILDCARD_NS NS record in wildcard domain '%1' +% DATASRC_MEMORY_MEM_WILDCARD_NS NS record in wildcard domain '%1' The software refuses to load NS records into a wildcard domain. It isn't explicitly forbidden, but the protocol is ambiguous about how this should behave and BIND 9 refuses that as well. Please describe your intention using different tools. -% DATASRC_MEM_WILDCARD_DNAME DNAME record in wildcard domain '%1' +% DATASRC_MEMORY_MEM_WILDCARD_DNAME DNAME record in wildcard domain '%1' The software refuses to load DNAME records into a wildcard domain. It isn't explicitly forbidden, but the protocol is ambiguous about how this should behave and BIND 9 refuses that as well. Please describe your intention using different tools. -% DATASRC_BAD_NSEC3_NAME NSEC3 record has a bad owner name '%1' +% DATASRC_MEMORY_BAD_NSEC3_NAME NSEC3 record has a bad owner name '%1' The software refuses to load NSEC3 records into a wildcard domain or the owner name has two or more labels below the zone origin. It isn't explicitly forbidden, but no sane zone wouldn have such names for NSEC3. BIND 9 also refuses NSEC3 at wildcard, so this behavior is compatible with BIND 9. -% DATASRC_MEM_ADD_RRSET adding RRset '%1/%2' into zone '%3' +% DATASRC_MEMORY_MEM_ADD_RRSET adding RRset '%1/%2' into zone '%3' Debug information. An RRset is being added to the in-memory data source. -% DATASRC_MEM_DUP_RRSET duplicate RRset '%1/%2' +% DATASRC_MEMORY_MEM_DUP_RRSET duplicate RRset '%1/%2' An RRset is being inserted into in-memory data source for a second time. The original version must be removed first. Note that loading master files where an RRset is split into multiple locations is not supported yet. -% DATASRC_MEM_NO_NSEC3PARAM NSEC3PARAM is missing for NSEC3-signed zone %1/%2 +% DATASRC_MEMORY_MEM_NO_NSEC3PARAM NSEC3PARAM is missing for NSEC3-signed zone %1/%2 The in-memory data source has loaded a zone signed with NSEC3 RRs, but it doesn't have a NSEC3PARAM RR at the zone origin. It's likely that the zone is somehow broken, but this RR is not necessarily needed for @@ -79,12 +79,12 @@ handling lookups with NSEC3 in this data source, so it accepts the given content of the zone. Nevertheless the administrator should look into the integrity of the zone data. -% DATASRC_MEM_ADD_ZONE adding zone '%1/%2' +% DATASRC_MEMORY_MEM_ADD_ZONE adding zone '%1/%2' Debug information. A zone is being added into the in-memory data source. -% DATASRC_MEM_FIND_ZONE looking for zone '%1' +% DATASRC_MEMORY_MEM_FIND_ZONE looking for zone '%1' Debug information. A zone object for this zone is being searched for in the in-memory data source. -% DATASRC_MEM_LOAD loading zone '%1' from file '%2' +% DATASRC_MEMORY_MEM_LOAD loading zone '%1' from file '%2' Debug information. The content of master file is being loaded into the memory. From 4171ba94fead572dbc4de66f9a35380680d55fa7 Mon Sep 17 00:00:00 2001 From: Mukund Sivaraman Date: Mon, 10 Sep 2012 12:43:10 +0530 Subject: [PATCH 136/199] [2108] Wrap lines --- src/lib/datasrc/memory/memory_client.cc | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/src/lib/datasrc/memory/memory_client.cc b/src/lib/datasrc/memory/memory_client.cc index 32b9d2b128..7758e3cae9 100644 --- a/src/lib/datasrc/memory/memory_client.cc +++ b/src/lib/datasrc/memory/memory_client.cc @@ -147,8 +147,8 @@ public: l > origin_labels; --l, wname = wname.split(1)) { if (wname.isWildcard()) { - LOG_DEBUG(logger, DBG_TRACE_DATA, DATASRC_MEMORY_MEM_ADD_WILDCARD). - arg(name); + LOG_DEBUG(logger, DBG_TRACE_DATA, + DATASRC_MEMORY_MEM_ADD_WILDCARD).arg(name); // Ensure a separate level exists for the "wildcarding" name, // and mark the node as "wild". @@ -189,7 +189,8 @@ public: } } else if ((rrset.getType() != RRType::NSEC()) && (RdataSet::find(set, RRType::CNAME()) != NULL)) { - LOG_ERROR(logger, DATASRC_MEMORY_MEM_CNAME_COEXIST).arg(rrset.getName()); + LOG_ERROR(logger, + DATASRC_MEMORY_MEM_CNAME_COEXIST).arg(rrset.getName()); isc_throw(AddError, "CNAME and " << rrset.getType() << " can't coexist for " << rrset.getName()); } @@ -233,7 +234,8 @@ public: // XXX: this is not only for CNAME or DNAME. We should generalize // this code for all other "singleton RR types" (such as SOA) in a // separate task. - LOG_ERROR(logger, DATASRC_MEMORY_MEM_SINGLETON).arg(rrset->getName()). + LOG_ERROR(logger, + DATASRC_MEMORY_MEM_SINGLETON).arg(rrset->getName()). arg(rrset->getType()); isc_throw(AddError, "multiple RRs of singleton type for " << rrset->getName()); @@ -251,7 +253,8 @@ public: if (compare.getRelation() != NameComparisonResult::SUPERDOMAIN && compare.getRelation() != NameComparisonResult::EQUAL) { - LOG_ERROR(logger, DATASRC_MEMORY_MEM_OUT_OF_ZONE).arg(rrset->getName()). + LOG_ERROR(logger, + DATASRC_MEMORY_MEM_OUT_OF_ZONE).arg(rrset->getName()). arg(zone_name); isc_throw(OutOfZone, "The name " << rrset->getName() << " is not contained in zone " << zone_name); @@ -688,7 +691,8 @@ InMemoryClient::getZoneCount() const { isc::datasrc::memory::ZoneTable::FindResult InMemoryClient::findZone2(const isc::dns::Name& zone_name) const { - LOG_DEBUG(logger, DBG_TRACE_DATA, DATASRC_MEMORY_MEM_FIND_ZONE).arg(zone_name); + LOG_DEBUG(logger, DBG_TRACE_DATA, + DATASRC_MEMORY_MEM_FIND_ZONE).arg(zone_name); ZoneTable::FindResult result(impl_->zone_table_->findZone(zone_name)); return (result); } From dd5485e6973a8ebc6fc99727ac3bc8903c8fce3b Mon Sep 17 00:00:00 2001 From: Mukund Sivaraman Date: Mon, 10 Sep 2012 12:53:39 +0530 Subject: [PATCH 137/199] [2108] Add unittest for load() which accepts an iterator --- .../memory/tests/memory_client_unittest.cc | 69 +++++++++++++++++++ 1 file changed, 69 insertions(+) diff --git a/src/lib/datasrc/memory/tests/memory_client_unittest.cc b/src/lib/datasrc/memory/tests/memory_client_unittest.cc index ee747a31cc..b86a3bd05d 100644 --- a/src/lib/datasrc/memory/tests/memory_client_unittest.cc +++ b/src/lib/datasrc/memory/tests/memory_client_unittest.cc @@ -32,6 +32,8 @@ #include #include +#include + #include #include // for bad_alloc @@ -40,6 +42,7 @@ using namespace isc::dns; using namespace isc::dns::rdata; using namespace isc::datasrc; using namespace isc::datasrc::memory; +using namespace isc::testutils; namespace { // Memory segment specified for tests. It normally behaves like a "local" @@ -63,6 +66,44 @@ private: size_t throw_count_; }; +static const char* rrset_data[] = { + "example.org. 3600 IN SOA ns1.example.org. bugs.x.w.example.org. 68 3600 300 3600000 3600", + "a.example.org. 3600 IN A 192.168.0.1", + "a.example.org. 3600 IN MX 10 mail.example.org.", + NULL +}; + +class MockIterator : public ZoneIterator { +private: + MockIterator() : + rrset_data_ptr_(rrset_data) + { + } + + const char** rrset_data_ptr_; + +public: + virtual ConstRRsetPtr getNextRRset() { + if (*rrset_data_ptr_ == NULL) { + return (ConstRRsetPtr()); + } + + RRsetPtr result(textToRRset(*rrset_data_ptr_, + RRClass::IN(), Name("example.org"))); + rrset_data_ptr_++; + + return (result); + } + + virtual ConstRRsetPtr getSOA() const { + isc_throw(isc::NotImplemented, "Not implemented"); + } + + static ZoneIteratorPtr makeIterator(void) { + return (ZoneIteratorPtr(new MockIterator())); + } +}; + class MemoryClientTest : public ::testing::Test { protected: MemoryClientTest() : zclass_(RRClass::IN()), @@ -142,6 +183,34 @@ TEST_F(MemoryClientTest, load) { TEST_DATA_DIR "/example.org.zone"); } +TEST_F(MemoryClientTest, loadFromIterator) { + client_->load(Name("example.org"), + *MockIterator::makeIterator()); + + ZoneIteratorPtr iterator(client_->getIterator(Name("example.org"))); + + // First we have the SOA + ConstRRsetPtr rrset(iterator->getNextRRset()); + EXPECT_TRUE(rrset); + EXPECT_EQ(RRType::SOA(), rrset->getType()); + + // RRType::MX() RRset + rrset = iterator->getNextRRset(); + EXPECT_TRUE(rrset); + EXPECT_EQ(RRType::MX(), rrset->getType()); + + // RRType::A() RRset + rrset = iterator->getNextRRset(); + EXPECT_TRUE(rrset); + EXPECT_EQ(RRType::A(), rrset->getType()); + + // There's nothing else in this iterator + EXPECT_EQ(ConstRRsetPtr(), iterator->getNextRRset()); + + // Iterating past the end should result in an exception + EXPECT_THROW(iterator->getNextRRset(), isc::Unexpected); +} + TEST_F(MemoryClientTest, loadNSEC3Signed) { client_->load(Name("example.org"), TEST_DATA_DIR "/example.org-nsec3-signed.zone"); From f1f2184dc4023a2cdd4157aefd3bdafdb164d586 Mon Sep 17 00:00:00 2001 From: Mukund Sivaraman Date: Mon, 10 Sep 2012 13:11:13 +0530 Subject: [PATCH 138/199] [2108] Pass memory segment to InMemoryClient constructor --- src/lib/datasrc/memory/memory_client.cc | 11 +++++++---- src/lib/datasrc/memory/memory_client.h | 5 ++++- .../datasrc/memory/tests/memory_client_unittest.cc | 2 +- 3 files changed, 12 insertions(+), 6 deletions(-) diff --git a/src/lib/datasrc/memory/memory_client.cc b/src/lib/datasrc/memory/memory_client.cc index 7758e3cae9..5617690d29 100644 --- a/src/lib/datasrc/memory/memory_client.cc +++ b/src/lib/datasrc/memory/memory_client.cc @@ -87,7 +87,9 @@ private: }; public: - InMemoryClientImpl(RRClass rrclass) : + InMemoryClientImpl(util::MemorySegment& mem_sgmt, + RRClass rrclass) : + local_mem_sgmt_(mem_sgmt), rrclass_(rrclass), zone_count_(0), zone_table_(ZoneTable::create(local_mem_sgmt_, rrclass)), @@ -106,7 +108,7 @@ public: // Memory segment to allocate/deallocate memory for the zone table. // (This will eventually have to be abstract; for now we hardcode the // specific derived segment class). - util::MemorySegmentLocal local_mem_sgmt_; + util::MemorySegment& local_mem_sgmt_; const RRClass rrclass_; unsigned int zone_count_; ZoneTable* zone_table_; @@ -671,8 +673,9 @@ generateRRsetFromIterator(ZoneIterator* iterator, LoadCallback callback) { } } -InMemoryClient::InMemoryClient(RRClass rrclass) : - impl_(new InMemoryClientImpl(rrclass)) +InMemoryClient::InMemoryClient(util::MemorySegment& mem_sgmt, + RRClass rrclass) : + impl_(new InMemoryClientImpl(mem_sgmt, rrclass)) {} InMemoryClient::~InMemoryClient() { diff --git a/src/lib/datasrc/memory/memory_client.h b/src/lib/datasrc/memory/memory_client.h index 0c211c1dd4..a28125950f 100644 --- a/src/lib/datasrc/memory/memory_client.h +++ b/src/lib/datasrc/memory/memory_client.h @@ -15,6 +15,8 @@ #ifndef DATASRC_MEMORY_CLIENT_H #define DATASRC_MEMORY_CLIENT_H 1 +#include + #include #include #include @@ -62,7 +64,8 @@ public: /// This constructor internally involves resource allocation, and if /// it fails, a corresponding standard exception will be thrown. /// It never throws an exception otherwise. - InMemoryClient(isc::dns::RRClass rrclass); + InMemoryClient(util::MemorySegment& mem_sgmt, + isc::dns::RRClass rrclass); /// The destructor. ~InMemoryClient(); diff --git a/src/lib/datasrc/memory/tests/memory_client_unittest.cc b/src/lib/datasrc/memory/tests/memory_client_unittest.cc index b86a3bd05d..0290a28cb4 100644 --- a/src/lib/datasrc/memory/tests/memory_client_unittest.cc +++ b/src/lib/datasrc/memory/tests/memory_client_unittest.cc @@ -107,7 +107,7 @@ public: class MemoryClientTest : public ::testing::Test { protected: MemoryClientTest() : zclass_(RRClass::IN()), - client_(new InMemoryClient(zclass_)) + client_(new InMemoryClient(mem_sgmt_, zclass_)) {} ~MemoryClientTest() { if (client_ != NULL) { From d976012056c29d07ad3ca563b7870a0ea07ca531 Mon Sep 17 00:00:00 2001 From: Mukund Sivaraman Date: Mon, 10 Sep 2012 13:15:30 +0530 Subject: [PATCH 139/199] [2108] Test for segment leaks due to allocation failures --- .../datasrc/memory/tests/memory_client_unittest.cc | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/lib/datasrc/memory/tests/memory_client_unittest.cc b/src/lib/datasrc/memory/tests/memory_client_unittest.cc index 0290a28cb4..8a7f19ebfa 100644 --- a/src/lib/datasrc/memory/tests/memory_client_unittest.cc +++ b/src/lib/datasrc/memory/tests/memory_client_unittest.cc @@ -211,6 +211,18 @@ TEST_F(MemoryClientTest, loadFromIterator) { EXPECT_THROW(iterator->getNextRRset(), isc::Unexpected); } +TEST_F(MemoryClientTest, loadMemoryAllocationFailures) { + // Just to check that things get cleaned up + + for (int i = 1; i < 16; i++) { + mem_sgmt_.setThrowCount(i); + EXPECT_THROW(client_->load(Name("example.org"), + TEST_DATA_DIR "/example.org.zone"), + std::bad_alloc); + } + // Teardown checks for memory segment leaks +} + TEST_F(MemoryClientTest, loadNSEC3Signed) { client_->load(Name("example.org"), TEST_DATA_DIR "/example.org-nsec3-signed.zone"); From e3f72ac2caea7a75deb1b772d9681d6be3aad203 Mon Sep 17 00:00:00 2001 From: Mukund Sivaraman Date: Mon, 10 Sep 2012 13:16:35 +0530 Subject: [PATCH 140/199] [2108] Remove TODO list --- src/lib/datasrc/memory/tests/memory_client_unittest.cc | 6 ------ 1 file changed, 6 deletions(-) diff --git a/src/lib/datasrc/memory/tests/memory_client_unittest.cc b/src/lib/datasrc/memory/tests/memory_client_unittest.cc index 8a7f19ebfa..86dd674a5a 100644 --- a/src/lib/datasrc/memory/tests/memory_client_unittest.cc +++ b/src/lib/datasrc/memory/tests/memory_client_unittest.cc @@ -738,10 +738,4 @@ TEST_F(MemoryClientTest, getJournalReaderNotImplemented) { EXPECT_THROW(client_->getJournalReader(Name("."), 0, 0), isc::NotImplemented); } - -// TODO: -// Add test for add() with separate_rrs=true -// Add test for ZoneIterator variant of load() -// Add test for leaks during allocation failures using TestMemorySegment -// Iterate through an entire zone } From 0ebd6dbbd9af7ea20543a9813a9d38cc7a227935 Mon Sep 17 00:00:00 2001 From: Marcin Siodelski Date: Mon, 10 Sep 2012 12:14:19 +0200 Subject: [PATCH 141/199] [2230] Added perfdhcp command line examples. --- tests/tools/dhcp-ubench/dhcp-perf-guide.html | 234 ++++++++++++++-- tests/tools/dhcp-ubench/dhcp-perf-guide.xml | 265 +++++++++++++++++-- 2 files changed, 460 insertions(+), 39 deletions(-) diff --git a/tests/tools/dhcp-ubench/dhcp-perf-guide.html b/tests/tools/dhcp-ubench/dhcp-perf-guide.html index 60de83809b..27da0b1c0d 100644 --- a/tests/tools/dhcp-ubench/dhcp-perf-guide.html +++ b/tests/tools/dhcp-ubench/dhcp-perf-guide.html @@ -7,7 +7,7 @@ This document describes various aspects of DHCP performance, measurements and tuning. It covers BIND 10 DHCP (codename Kea), existing ISC DHCP4 software, perfdhcp (a DHCP performance - measurement tool) and other related topics.


Preface

Table of Contents

Acknowledgements

Acknowledgements

ISC would like to acknowledge generous support for + measurement tool) and other related topics.


Preface

Table of Contents

Acknowledgements

Acknowledgements

ISC would like to acknowledge generous support for BIND 10 development of DHCPv4 and DHCPv6 components provided by Comcast.

Chapter 1. Introduction

This document is in its early stages of development. It is @@ -196,7 +196,7 @@ SQLite version: 3.7.9sourceid version is 2011-11-01 00:52:41 c7c6050ef060877ebe7 Multi-threaded or multi-process benchmark may be considered in the future. It may be somewhat difficult as only some backends support concurrent access. -

Chapter 4. perfdhcp

Purpose

+

Chapter 4. perfdhcp

Purpose

There is a growing need to evaluate performance of DHCP servers in different traffic conditions to understand their bottle necks. This helps to elimante bugs in existing DHCP software as well @@ -206,15 +206,15 @@ SQLite version: 3.7.9sourceid version is 2011-11-01 00:52:41 c7c6050ef060877ebe7 mostly. However, the number of implemented features and parameters exposed to the user make this tool useful for functional testing as well. -

Key features

+

Key features

The perfdhcp exposes the number of command line parameters to control DHCP message exchanges. Currently they fall back to the following categories:

  1. Rate control - control how many DHCP exchanges are initiated within a period of time. Tool can also simulate - best effort conditions where it attempts to start as many - exchanges as possible. + best effort conditions attempting to initiate as many DHCP + packet exchanges within a unit of time as possible.

  2. Test exit specifiers - control the conditions when test completes including number of initiated exchanges, test period or @@ -367,7 +367,7 @@ The exit status is: exchanges are not successfully completed.

    -

Running a test

+

Starting perfdhcp

In order to run performance test at least two separate systems have to be installed: client and server. The first one has to have perfdhcp tool installed, the latter has to have DHCP server @@ -377,28 +377,224 @@ The exit status is: impacted by the performance of VMs.

The DHCP operates on low port numbers (67 for DHCPv4 relays and - 547 for DHCPv6) running perfdhcp with non-root priviliges will + 547 for DHCPv6). Running perfdhcp with non-root priviliges will usually result in the error message similar to this:

$./perfdhcp -4 -l eth3 -r 100 all
 Error running perfdhcp: Failed to bind socket 3 to 172.16.1.2/port=67
         

- perfdhcp exposes '-L' command line option that - imposes use of custom local port and the following command line will - work: + perfdhcp has the '-L' command line option that + imposes use of custom local port. Thus the following command + line will work:

$./perfdhcp -4 -l eth3 -r 100 -L 10067 all

but in the standard configuration no responses will be received from the ISC DHCP server because server responds to default relay port 67. + Alternative way to overcome this issue is to run perfdhcp as root.

- Alternative way to overcome the issue with lack of privileges - to use default DHCP port number is to run perfdhcp as root. -

- In this section the perfdhcp command line options examples + Currently, perfdhcp is seen from the server perspective as relay agent. + This simplifies its implementation (specifically there is no need to + receive traffic sent to braodcast addresses). This imposes that IPv4 + address has to be set manually on the interface that will be used to + communicate with the server. For example, if DHCPv4 server is listening + on the interface connected to 172.16.1.0 subnet, interface on client + machine has to have network address assigned from the same subnet + on one of its interfaces connected to this subnet: +

#ifconfig eth3 172.16.1.2. netmask 255.255.255.0 up

+

perfdhcp command line examples

+ In this section the perfdhcp command line examples are presented as a quick start guide for new users. For the detailed list of command line options refer to the section called “Command line options”. -

-

-          
-        

-

\ No newline at end of file +

Example: basic usage

+ If server is listening on interface with IPv4 address 172.16.1.1 + the simpliest perfdhcp command line will look like this: +

#./perfdhcp 172.16.1.1
+***Rate statistics***
+Rate: 206.345
+
+***Statistics for: DISCOVER-OFFER***
+sent packets: 21641
+received packets: 350
+drops: 21291
+orphans: 0
+
+min delay: 9.022 ms
+avg delay: 143.100 ms
+max delay: 259.303 ms
+std deviation: 56.074 ms
+collected packets: 30
+
+***Statistics for: REQUEST-ACK***
+sent packets: 350
+received packets: 268
+drops: 82
+orphans: 0
+
+min delay: 3.010 ms
+avg delay: 152.470 ms
+max delay: 258.634 ms
+std deviation: 56.936 ms
+collected packets: 0
+          

+ In this case perfdhcp will use remote address 172.16.1.1 as a + destination address and will use suitable local interface for + communication. Since, no rate control parameters have been specified + it will be initiating DHCP exchanges with the maximum possible + rate (it will try to initiate maximum number of exchanges per + second and count number of completed exchanged). Due to server's + performance constraints, many DHCP packets sent to server are likely + to be dropped. The performance test will be running until it is + not interrupted by the user (with ^C). +

+ The default performance statistics reported by perfdhcp have the + following meaning: +

  • Rate - number of packet exchanges (packet sent + to the server and matching response received from the server) + completed within a second.

  • sent packets - total number of DHCP packets of + a specific type sent to the server.

  • received packets - total number of DHCP packets + of specific type received from the server.

  • drops - number of dropped packets for the + particular exchange. Number of dropped packets is calculated as + a difference between number of sent packets and number of + response packets received from the server. It is likely that + server sent the reponse but perfdhcp execution had ended before + reponse arrived. In such case this packet will be assumed + dropped.

  • orphans - number of packets that have been + received from the server and did not match any packet sent by + perfdhcp. This may occur if received packet has been sent + to some other host or if exchange time out has occured and + has been been garbage collected.

  • min delay - minimum delay that occured between + sending the packet to the server and receiving reponse from + it.

  • avg delay - average delay between sending the + packet of the specific type the server and receiving response + from it.

  • max delay - maximum delat that occured between + sedning the packet to the server and receiveing response from + it.

  • std deviation - standard deviation of delay + between sending the packet of a specific type to the server and + receiving response from it.

  • collected packets - number of garbage collected + sent packets. Packets may get garbage collected when waiting time + for server response exceeds value set with + -d<drop-time>.

+

+ perfdhcp allows to run the test using specified interface: +

#./perfdhcp -l eth3

+ or local address assigned to it: +

#./perfdhcp -l 172.16.1.2

+

Example: rate control

+ In the examples above perfdhcp initiates new exchanges with best + effort rate. In this case many packets is expected to be dropped by the + server due to performance limitations. Many times it is desired to set + the expected (reasonable) rate and verify if generated traffic is + handled without packet dropes by DHCP server. The following command will + make perfdhcp to initiate 300 4-way exchanges per second and test will + last for 60 seconds: +

#./perfdhcp -l eth3 -p 60 -r 300
+***Rate statistics***
+Rate: 256.683 exchanges/second, expected rate: 300 exchanges/second
+
+***Statistics for: DISCOVER-OFFER***
+sent packets: 17783
+received packets: 15401
+drops: 2382
+orphans: 0
+
+min delay: 0.109 ms
+avg delay: 75.756 ms
+max delay: 575.614 ms
+std deviation: 60.513 ms
+collected packets: 11
+
+***Statistics for: REQUEST-ACK***
+sent packets: 15401
+received packets: 15317
+drops: 84
+orphans: 0
+
+min delay: 0.536 ms
+avg delay: 72.072 ms
+max delay: 576.749 ms
+std deviation: 58.189 ms
+collected packets: 0
+          

+ Note that in this example the packet drops have been significantly + reduced thanks to setting reasonable rate. The non-zero number of + packet drops and achived rate (256/s) below expected rate (300/s) + indicate that server's measured performance is lower than 300 leases + per second. Further rate decrease should eliminate most of the packet + drops and bring achived rate close to expected rate: +

#./perfdhcp -l eth3 -p 60 -r 100 -R 30
+***Rate statistics***
+Rate: 99.8164 exchanges/second, expected rate: 100 exchanges/second
+
+***Statistics for: DISCOVER-OFFER***
+sent packets: 5989
+received packets: 5989
+drops: 0
+orphans: 0
+
+min delay: 0.023 ms
+avg delay: 2.198 ms
+max delay: 181.760 ms
+std deviation: 9.429 ms
+collected packets: 0
+
+***Statistics for: REQUEST-ACK***
+sent packets: 5989
+received packets: 5989
+drops: 0
+orphans: 0
+
+min delay: 0.473 ms
+avg delay: 2.355 ms
+max delay: 189.658 ms
+std deviation: 5.876 ms
+collected packets: 0
+          

+ Note that the last parameter (-R 30) configures perfdhcp to simulate + traffic from distinct 30 clients. +

Example: templates

+ By default the DHCP messages are formed in-flight with default options. + If desired, there is a way to define custom packet format with template + files. Content in template files is encoded in hexadecimal format. The + perfdhcp forms the packet by replacing parts of the binary stream read + from the file with variable data such as elapsed time, HW address, DUID + etc. The offsets where such variable data is placed is specific to the + template file and have to be specified from the command line. Refer to + the section called “Command line options” to find out how to + specify offsets for particular options and fields. With the following + command line the DHCPv6 SOLICIT and REQUEST packets will be formed from + solicit-example.hex and request6-example.hex packets: +

#./perfdhcp -6 -l eth3 -r 100 -R 20 -T templates/solicit-example.hex -T templates/request6-example.hex -O 21 -E 84 -S 22 -I 40 servers
+***Rate statistics***
+Rate: 99.5398 exchanges/second, expected rate: 100 exchanges/second
+
+***Statistics for: SOLICIT-ADVERTISE***
+sent packets: 570
+received packets: 569
+drops: 1
+orphans: 0
+
+min delay: 0.259 ms
+avg delay: 0.912 ms
+max delay: 6.979 ms
+std deviation: 0.709 ms
+collected packets: 0
+
+***Statistics for: REQUEST-REPLY***
+sent packets: 569
+received packets: 569
+drops: 0
+orphans: 0
+
+min delay: 0.084 ms
+avg delay: 0.607 ms
+max delay: 6.490 ms
+std deviation: 0.518 ms
+collected packets: 0
+          

+ where: +

  • two occurences of -O 21 - DUID's last octet + positions in SOLICIT and REQUEST respectively.

  • -E 84 - elapsed time option position in + REQUEST template

  • -S 22 - server id position in REQUEST + template

  • -I 40 - IA_NA option position in REQUEST + template

+

\ No newline at end of file diff --git a/tests/tools/dhcp-ubench/dhcp-perf-guide.xml b/tests/tools/dhcp-ubench/dhcp-perf-guide.xml index bfde03de31..f9b9744bf7 100644 --- a/tests/tools/dhcp-ubench/dhcp-perf-guide.xml +++ b/tests/tools/dhcp-ubench/dhcp-perf-guide.xml @@ -603,8 +603,7 @@ SQLite version: 3.7.9sourceid version is 2011-11-01 00:52:41 c7c6050ef060877ebe7 well. - -
+
Key features The perfdhcp exposes the number of command line parameters to @@ -615,8 +614,8 @@ SQLite version: 3.7.9sourceid version is 2011-11-01 00:52:41 c7c6050ef060877ebe7 Rate control - control how many DHCP exchanges are initiated within a period of time. Tool can also simulate - best effort conditions where it attempts to start as many - exchanges as possible. + best effort conditions attempting to initiate as many DHCP + packet exchanges within a unit of time as possible. @@ -661,7 +660,6 @@ SQLite version: 3.7.9sourceid version is 2011-11-01 00:52:41 c7c6050ef060877ebe7
-
Command line options @@ -793,8 +791,8 @@ The exit status is:
-
- Running a test +
+ Starting perfdhcp In order to run performance test at least two separate systems have to be installed: client and server. The first one has to have @@ -806,35 +804,262 @@ The exit status is: The DHCP operates on low port numbers (67 for DHCPv4 relays and - 547 for DHCPv6) running perfdhcp with non-root priviliges will + 547 for DHCPv6). Running perfdhcp with non-root priviliges will usually result in the error message similar to this: $./perfdhcp -4 -l eth3 -r 100 all Error running perfdhcp: Failed to bind socket 3 to 172.16.1.2/port=67 - perfdhcp exposes '-L' command line option that - imposes use of custom local port and the following command line will - work: + perfdhcp has the '-L' command line option that + imposes use of custom local port. Thus the following command + line will work: $./perfdhcp -4 -l eth3 -r 100 -L 10067 all but in the standard configuration no responses will be received from the ISC DHCP server because server responds to default relay port 67. + Alternative way to overcome this issue is to run perfdhcp as root. - Alternative way to overcome the issue with lack of privileges - to use default DHCP port number is to run perfdhcp as root. + Currently, perfdhcp is seen from the server perspective as relay agent. + This simplifies its implementation (specifically there is no need to + receive traffic sent to braodcast addresses). This imposes that IPv4 + address has to be set manually on the interface that will be used to + communicate with the server. For example, if DHCPv4 server is listening + on the interface connected to 172.16.1.0 subnet, interface on client + machine has to have network address assigned from the same subnet + on one of its interfaces connected to this subnet: + #ifconfig eth3 172.16.1.2. netmask 255.255.255.0 up - +
+
+ perfdhcp command line examples - In this section the perfdhcp command line options examples + In this section the perfdhcp command line examples are presented as a quick start guide for new users. For the detailed list of command line options refer to . - - - - - +
+ Example: basic usage + + If server is listening on interface with IPv4 address 172.16.1.1 + the simpliest perfdhcp command line will look like this: + #./perfdhcp 172.16.1.1 +***Rate statistics*** +Rate: 206.345 + +***Statistics for: DISCOVER-OFFER*** +sent packets: 21641 +received packets: 350 +drops: 21291 +orphans: 0 + +min delay: 9.022 ms +avg delay: 143.100 ms +max delay: 259.303 ms +std deviation: 56.074 ms +collected packets: 30 + +***Statistics for: REQUEST-ACK*** +sent packets: 350 +received packets: 268 +drops: 82 +orphans: 0 + +min delay: 3.010 ms +avg delay: 152.470 ms +max delay: 258.634 ms +std deviation: 56.936 ms +collected packets: 0 + + In this case perfdhcp will use remote address 172.16.1.1 as a + destination address and will use suitable local interface for + communication. Since, no rate control parameters have been specified + it will be initiating DHCP exchanges with the maximum possible + rate (it will try to initiate maximum number of exchanges per + second and count number of completed exchanged). Due to server's + performance constraints, many DHCP packets sent to server are likely + to be dropped. The performance test will be running until it is + not interrupted by the user (with ^C). + + + The default performance statistics reported by perfdhcp have the + following meaning: + + Rate - number of packet exchanges (packet sent + to the server and matching response received from the server) + completed within a second. + sent packets - total number of DHCP packets of + a specific type sent to the server. + received packets - total number of DHCP packets + of specific type received from the server. + drops - number of dropped packets for the + particular exchange. Number of dropped packets is calculated as + a difference between number of sent packets and number of + response packets received from the server. It is likely that + server sent the reponse but perfdhcp execution had ended before + reponse arrived. In such case this packet will be assumed + dropped. + orphans - number of packets that have been + received from the server and did not match any packet sent by + perfdhcp. This may occur if received packet has been sent + to some other host or if exchange time out has occured and + has been been garbage collected. + min delay - minimum delay that occured between + sending the packet to the server and receiving reponse from + it. + avg delay - average delay between sending the + packet of the specific type the server and receiving response + from it. + max delay - maximum delat that occured between + sedning the packet to the server and receiveing response from + it. + std deviation - standard deviation of delay + between sending the packet of a specific type to the server and + receiving response from it. + collected packets - number of garbage collected + sent packets. Packets may get garbage collected when waiting time + for server response exceeds value set with + ]]>. + + + + perfdhcp allows to run the test using specified interface: + #./perfdhcp -l eth3 + or local address assigned to it: + #./perfdhcp -l 172.16.1.2 + +
+
+ Example: rate control + + In the examples above perfdhcp initiates new exchanges with best + effort rate. In this case many packets is expected to be dropped by the + server due to performance limitations. Many times it is desired to set + the expected (reasonable) rate and verify if generated traffic is + handled without packet dropes by DHCP server. The following command will + make perfdhcp to initiate 300 4-way exchanges per second and test will + last for 60 seconds: + #./perfdhcp -l eth3 -p 60 -r 300 +***Rate statistics*** +Rate: 256.683 exchanges/second, expected rate: 300 exchanges/second + +***Statistics for: DISCOVER-OFFER*** +sent packets: 17783 +received packets: 15401 +drops: 2382 +orphans: 0 + +min delay: 0.109 ms +avg delay: 75.756 ms +max delay: 575.614 ms +std deviation: 60.513 ms +collected packets: 11 + +***Statistics for: REQUEST-ACK*** +sent packets: 15401 +received packets: 15317 +drops: 84 +orphans: 0 + +min delay: 0.536 ms +avg delay: 72.072 ms +max delay: 576.749 ms +std deviation: 58.189 ms +collected packets: 0 + + Note that in this example the packet drops have been significantly + reduced thanks to setting reasonable rate. The non-zero number of + packet drops and achived rate (256/s) below expected rate (300/s) + indicate that server's measured performance is lower than 300 leases + per second. Further rate decrease should eliminate most of the packet + drops and bring achived rate close to expected rate: + #./perfdhcp -l eth3 -p 60 -r 100 -R 30 +***Rate statistics*** +Rate: 99.8164 exchanges/second, expected rate: 100 exchanges/second + +***Statistics for: DISCOVER-OFFER*** +sent packets: 5989 +received packets: 5989 +drops: 0 +orphans: 0 + +min delay: 0.023 ms +avg delay: 2.198 ms +max delay: 181.760 ms +std deviation: 9.429 ms +collected packets: 0 + +***Statistics for: REQUEST-ACK*** +sent packets: 5989 +received packets: 5989 +drops: 0 +orphans: 0 + +min delay: 0.473 ms +avg delay: 2.355 ms +max delay: 189.658 ms +std deviation: 5.876 ms +collected packets: 0 + + Note that the last parameter (-R 30) configures perfdhcp to simulate + traffic from distinct 30 clients. + +
+
+ Example: templates + + By default the DHCP messages are formed in-flight with default options. + If desired, there is a way to define custom packet format with template + files. Content in template files is encoded in hexadecimal format. The + perfdhcp forms the packet by replacing parts of the binary stream read + from the file with variable data such as elapsed time, HW address, DUID + etc. The offsets where such variable data is placed is specific to the + template file and have to be specified from the command line. Refer to + to find out how to + specify offsets for particular options and fields. With the following + command line the DHCPv6 SOLICIT and REQUEST packets will be formed from + solicit-example.hex and request6-example.hex packets: + #./perfdhcp -6 -l eth3 -r 100 -R 20 -T templates/solicit-example.hex -T templates/request6-example.hex -O 21 -E 84 -S 22 -I 40 servers +***Rate statistics*** +Rate: 99.5398 exchanges/second, expected rate: 100 exchanges/second + +***Statistics for: SOLICIT-ADVERTISE*** +sent packets: 570 +received packets: 569 +drops: 1 +orphans: 0 + +min delay: 0.259 ms +avg delay: 0.912 ms +max delay: 6.979 ms +std deviation: 0.709 ms +collected packets: 0 + +***Statistics for: REQUEST-REPLY*** +sent packets: 569 +received packets: 569 +drops: 0 +orphans: 0 + +min delay: 0.084 ms +avg delay: 0.607 ms +max delay: 6.490 ms +std deviation: 0.518 ms +collected packets: 0 + + where: + + two occurences of -O 21 - DUID's last octet + positions in SOLICIT and REQUEST respectively. + -E 84 - elapsed time option position in + REQUEST template + -S 22 - server id position in REQUEST + template + -I 40 - IA_NA option position in REQUEST + template + + +
From 6febded786acd66e509faf6cc31f78a019609666 Mon Sep 17 00:00:00 2001 From: Marcin Siodelski Date: Mon, 10 Sep 2012 15:37:23 +0200 Subject: [PATCH 142/199] [2230] Added initial version of perfdhcp classes description doc. --- tests/tools/perfdhcp/perfdhcp-classes.html | 29 ++++++++ tests/tools/perfdhcp/perfdhcp-classes.xml | 81 ++++++++++++++++++++++ 2 files changed, 110 insertions(+) create mode 100644 tests/tools/perfdhcp/perfdhcp-classes.html create mode 100644 tests/tools/perfdhcp/perfdhcp-classes.xml diff --git a/tests/tools/perfdhcp/perfdhcp-classes.html b/tests/tools/perfdhcp/perfdhcp-classes.html new file mode 100644 index 0000000000..599bf46e8f --- /dev/null +++ b/tests/tools/perfdhcp/perfdhcp-classes.html @@ -0,0 +1,29 @@ + + + perfdhcp objects breakdown

perfdhcp objects breakdown

Marcin Siodelski

This is a companion document for BIND 10 version + 20120712.

Abstract

This document briefly describes C++ classes being part of the + perfdhcp tool.


Chapter 1. Classes

CommandOptions (command_options (.h, cc)

+ CommandOptions is a singleton class that parses perfdhcp command line + parameters and initializes its members accordingly. If parsed parameters + are invalid the parser function throws exception. +

+main(int argc, char* argv[]) {
+    try {
+        CommandOptions& command_options = CommandOptions::instance();
+        command_options.parse(argc, argv);
+    catch(const Exception& e) {
+        ...
+    }
+    ...
+}
+        

+ If argument parsing is successful than parsed values can be read from + CommandOptions singleton from any class or function in the program: +

+
+    ...
+    int rate = CommandOptions::instance().getRate();
+    ...
+
+        

+

\ No newline at end of file diff --git a/tests/tools/perfdhcp/perfdhcp-classes.xml b/tests/tools/perfdhcp/perfdhcp-classes.xml new file mode 100644 index 0000000000..13da7c6501 --- /dev/null +++ b/tests/tools/perfdhcp/perfdhcp-classes.xml @@ -0,0 +1,81 @@ + + + +%version; +]> + + + + + + + + perfdhcp objects breakdown + + + + 2012 + Internet Systems Consortium, Inc. ("ISC") + + + Marcin + Siodelski + + + This document briefly describes C++ classes being part of the + perfdhcp tool. + + + This is a companion document for BIND 10 version + &__VERSION__;. + + + + + Classes +
+ CommandOptions (command_options (.h, cc) + + CommandOptions is a singleton class that parses perfdhcp command line + parameters and initializes its members accordingly. If parsed parameters + are invalid the parser function throws exception. + + + + If argument parsing is successful than parsed values can be read from + CommandOptions singleton from any class or function in the program: + + + + +
+
+
From 8f915dfd4719f03ba41c58b0524ab05054e0628c Mon Sep 17 00:00:00 2001 From: Marcin Siodelski Date: Mon, 10 Sep 2012 17:50:55 +0200 Subject: [PATCH 143/199] [2230] Added description of all major perfdhcp classes and intro. --- tests/tools/perfdhcp/perfdhcp-classes.html | 121 ++++++++++++++-- tests/tools/perfdhcp/perfdhcp-classes.xml | 159 +++++++++++++++++++-- 2 files changed, 256 insertions(+), 24 deletions(-) diff --git a/tests/tools/perfdhcp/perfdhcp-classes.html b/tests/tools/perfdhcp/perfdhcp-classes.html index 599bf46e8f..d4dd2e5a9d 100644 --- a/tests/tools/perfdhcp/perfdhcp-classes.html +++ b/tests/tools/perfdhcp/perfdhcp-classes.html @@ -1,29 +1,126 @@ - perfdhcp objects breakdown

perfdhcp objects breakdown

Marcin Siodelski

This is a companion document for BIND 10 version - 20120712.

Abstract

This document briefly describes C++ classes being part of the - perfdhcp tool.


Chapter 1. Classes

CommandOptions (command_options (.h, cc)

+ perfdhcp objects breakdown

perfdhcp objects breakdown

Marcin Siodelski

This is a companion document for BIND 10 version + 20120817.

Abstract

This document briefly describes C++ classes being part of the + perfdhcp tool.


Chapter 1. Introduction

+ The source code of perfdhcp tool has been refactored from C to C++. + There is a bunch of new C++ classes. in the refactored code. + All of them are well documented in Doxygen. This document is + limited to brief overview of major classes to help understand + their purpose. +

Chapter 2. Classes

CommandOptions (command_options.h .cc)

CommandOptions is a singleton class that parses perfdhcp command line parameters and initializes its members accordingly. If parsed parameters are invalid the parser function throws exception.

 main(int argc, char* argv[]) {
-    try {
-        CommandOptions& command_options = CommandOptions::instance();
-        command_options.parse(argc, argv);
-    catch(const Exception& e) {
-        ...
-    }
+try {
+    CommandOptions& command_options = CommandOptions::instance();
+    command_options.parse(argc, argv);
+catch(const Exception& e) {
     ...
+}
+...
 }
         

If argument parsing is successful than parsed values can be read from CommandOptions singleton from any class or function in the program:

 
-    ...
-    int rate = CommandOptions::instance().getRate();
-    ...
+...
+int rate = CommandOptions::instance().getRate();
+...
 
         

+

TestControl (test_control.h .cc)

+ TestControl singleton is responsible for test execution coordination. + It relies on CommandOptions object to get all required test parameters. For + this reason CommandOptions has to be initialized and CommandOptions::parse() + has to be called prior to calling TestControl::run(). The latter function + performs initialization of TestControl internals and execues the main program + loop. The TestControl::run() function performs the following major operations: +

  1. check if command line has been parsed,

  2. prints diagnostics if specified from command line,

  3. register DHCP options factory functions,

  4. read packet templates from files,

  5. initialize Statistics Manager object,

  6. set interrupt signal handler (handle ^C),

  7. open and close socket for communication with server,

  8. coordinate sending and receiving packets,

  9. coordinate intermediate reporting,

  10. prints test statistics.

+

+ TestControl is a singleton object so there is one sole instance of + it throughout the program. In order to allow running performance test + multiple times (using different command line options) with single instance + of the TestControl object it uses TestControl::reset() function internally + to reset state of the class members. Also, functions that initialize + various class members like Statistics Manager will release any objects + existing from previous test runs. +

StatsMgr (stats_mgr.h)

+ StatsMgr is a class that holds all performance statistics gathered throughout + the test execution. Statistics Manager is created in TestControl. The + TestControl class posts all sent and received packets to StatsMgr. + Collected packets are used to match packets received from the server with + corresponding sent packets, calculate round trip time, number of packet + drops etc. Apart from the standard counters implemented in StatsMgr, + custom (named) counters can be specified and incremented by the calling + class. StatsMgr also exposes multiple functions that print gathered + statistics into the console. +

+ The StatsMgr is a template class that takes Pkt4, Pkt6, PerfPkt4 or + PerfPkt6 as a typename. The instance of the StatsMgr can be created + as follows: +

+
+typedef StatsMgr<Pkt4> StatsMgr4;
+StatsMgr4 stats_mgr4 = boost::shared_ptr<StatsMgr4>(new StatsMgr4());
+try {
+    stats_mgr->addExchangeStats(StatsMgr4::XCHG_DO);
+} catch(const Exception& e) {
+    std::cout << e.what() << std::endl;
+}
+
+        

+ The StatsMgr instance created in the example above will be used for + DHCPv4 testing (collect DHCPv4 packets) and will be configured to + monitor statistics for DISCOVER-OFFER packet exchanges. +

PerfPkt4, PerfPkt6 (perf_pkt4.h .cc, perf_pkt6.h .cc)

+ The PerfPkt4 and PerfPkt6 classes are derived from dhcp::Pkt4 and + dhcp::Pkt6 (src/lib/dhcp). They extend parent class functionality + by adding support for template files. The instance of these classes + can be created using raw buffer (read from packet template file). + Once packet object is initialized it is possible to replace + parts of the on-wire data by using LocalizedOption mechanism. +

LocalizedOption (localized_option.h)

+ LocalizedOption derives from dhcp::Option class. It represents the + DHCP option (v4 or v6) to be placed at specified position in the + packet buffer (see the section called “PerfPkt4, PerfPkt6 (perf_pkt4.h .cc, perf_pkt6.h .cc)”). Such option is added + to option collection in PerfPkt4 or PerfPkt6 object and when any of + PerfPktX::rawPack() function is called their content is stored in + the packet output buffer at the position pointed to by + LocalizedOption object. The LocalizedOption also allows to read the + on wire data in received packet at the specified position. In this + case LocalizedOption has to be created and added to recived packet. + When PerfPktX::rawUnpack() is called the contents of the buffer + will be read and stored in LocalizedOption object for further + processing. +

+ The following code shows how to create a packet from (template) + buffer and replace option data in the buffer with LocalizedOption. +

+
+OptionBuffer buf;
+// fill buf with data here.
+...
+boost::scoped_ptr<PerfPkt4> pkt(new PerfPkt4(&buf[0], buf.size());
+const size_t offset_hostname = 240;
+OptionBuffer vec_hostname;
+// fill the hostname vector with data
+...
+LocalizedOptionPtr opt_hostname(new LocalizedOption(Option::V4, DHO_HOST_NAME, vec_hostname,
+                                                    offset_hostname));
+pkt->addOption(opt_hostname);
+// by calling rawPack() we replace packet contents with option contents at
+// buffer position 240.
+pkt->rawPack();
+
+        

+

PktTransform (pkt_transform.h .cc)

+ This helper class contains the static functions to pack and unpack + DHCP options (specifically LocalizedOption) to and from the packet + buffer. This logic has been moved away from PerfPkt4 and PerfPkt6 + classes to PktTransform because PerfPktX classes share the logic + here.

\ No newline at end of file diff --git a/tests/tools/perfdhcp/perfdhcp-classes.xml b/tests/tools/perfdhcp/perfdhcp-classes.xml index 13da7c6501..8dda8b72a4 100644 --- a/tests/tools/perfdhcp/perfdhcp-classes.xml +++ b/tests/tools/perfdhcp/perfdhcp-classes.xml @@ -2,7 +2,7 @@ - + %version; ]> @@ -22,7 +22,7 @@ - PERFORMANCE OF THIS SOFTWARE. --> - + @@ -47,35 +47,170 @@ + + Introduction + + The source code of perfdhcp tool has been refactored from C to C++. + There is a bunch of new C++ classes. in the refactored code. + All of them are well documented in Doxygen. This document is + limited to brief overview of major classes to help understand + their purpose. + + Classes
- CommandOptions (command_options (.h, cc) + CommandOptions (command_options.h .cc) CommandOptions is a singleton class that parses perfdhcp command line parameters and initializes its members accordingly. If parsed parameters are invalid the parser function throws exception. If argument parsing is successful than parsed values can be read from CommandOptions singleton from any class or function in the program:
+
+ TestControl (test_control.h .cc) + + TestControl singleton is responsible for test execution coordination. + It relies on CommandOptions object to get all required test parameters. For + this reason CommandOptions has to be initialized and CommandOptions::parse() + has to be called prior to calling TestControl::run(). The latter function + performs initialization of TestControl internals and execues the main program + loop. The TestControl::run() function performs the following major operations: + + check if command line has been parsed, + prints diagnostics if specified from command line, + register DHCP options factory functions, + read packet templates from files, + initialize Statistics Manager object, + set interrupt signal handler (handle ^C), + open and close socket for communication with server, + coordinate sending and receiving packets, + coordinate intermediate reporting, + prints test statistics. + + + + TestControl is a singleton object so there is one sole instance of + it throughout the program. In order to allow running performance test + multiple times (using different command line options) with single instance + of the TestControl object it uses TestControl::reset() function internally + to reset state of the class members. Also, functions that initialize + various class members like Statistics Manager will release any objects + existing from previous test runs. + +
+
+ StatsMgr (stats_mgr.h) + + StatsMgr is a class that holds all performance statistics gathered throughout + the test execution. Statistics Manager is created in TestControl. The + TestControl class posts all sent and received packets to StatsMgr. + Collected packets are used to match packets received from the server with + corresponding sent packets, calculate round trip time, number of packet + drops etc. Apart from the standard counters implemented in StatsMgr, + custom (named) counters can be specified and incremented by the calling + class. StatsMgr also exposes multiple functions that print gathered + statistics into the console. + + + The StatsMgr is a template class that takes Pkt4, Pkt6, PerfPkt4 or + PerfPkt6 as a typename. The instance of the StatsMgr can be created + as follows: + + StatsMgr4; +StatsMgr4 stats_mgr4 = boost::shared_ptr(new StatsMgr4()); +try { + stats_mgr->addExchangeStats(StatsMgr4::XCHG_DO); +} catch(const Exception& e) { + std::cout << e.what() << std::endl; +} +]]> + + The StatsMgr instance created in the example above will be used for + DHCPv4 testing (collect DHCPv4 packets) and will be configured to + monitor statistics for DISCOVER-OFFER packet exchanges. + +
+
+ PerfPkt4, PerfPkt6 (perf_pkt4.h .cc, perf_pkt6.h .cc) + + The PerfPkt4 and PerfPkt6 classes are derived from dhcp::Pkt4 and + dhcp::Pkt6 (src/lib/dhcp). They extend parent class functionality + by adding support for template files. The instance of these classes + can be created using raw buffer (read from packet template file). + Once packet object is initialized it is possible to replace + parts of the on-wire data by using LocalizedOption mechanism. + +
+
+ LocalizedOption (localized_option.h) + + LocalizedOption derives from dhcp::Option class. It represents the + DHCP option (v4 or v6) to be placed at specified position in the + packet buffer (see ). Such option is added + to option collection in PerfPkt4 or PerfPkt6 object and when any of + PerfPktX::rawPack() function is called their content is stored in + the packet output buffer at the position pointed to by + LocalizedOption object. The LocalizedOption also allows to read the + on wire data in received packet at the specified position. In this + case LocalizedOption has to be created and added to recived packet. + When PerfPktX::rawUnpack() is called the contents of the buffer + will be read and stored in LocalizedOption object for further + processing. + + + The following code shows how to create a packet from (template) + buffer and replace option data in the buffer with LocalizedOption. + + pkt(new PerfPkt4(&buf[0], buf.size()); +const size_t offset_hostname = 240; +OptionBuffer vec_hostname; +// fill the hostname vector with data +... +LocalizedOptionPtr opt_hostname(new LocalizedOption(Option::V4, DHO_HOST_NAME, vec_hostname, + offset_hostname)); +pkt->addOption(opt_hostname); +// by calling rawPack() we replace packet contents with option contents at +// buffer position 240. +pkt->rawPack(); +]]> + + +
+
+ PktTransform (pkt_transform.h .cc) + + This helper class contains the static functions to pack and unpack + DHCP options (specifically LocalizedOption) to and from the packet + buffer. This logic has been moved away from PerfPkt4 and PerfPkt6 + classes to PktTransform because PerfPktX classes share the logic + here. + +
From 73291cf04175391585c0222f302150d9bc66a61c Mon Sep 17 00:00:00 2001 From: Jelte Jansen Date: Tue, 11 Sep 2012 12:19:46 +0200 Subject: [PATCH 144/199] [2151] change name to LabelSequence::WILDCARD --- src/lib/dns/labelsequence.h | 4 ++-- src/lib/dns/tests/labelsequence_unittest.cc | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/lib/dns/labelsequence.h b/src/lib/dns/labelsequence.h index b9105506c1..3f6a03aea7 100644 --- a/src/lib/dns/labelsequence.h +++ b/src/lib/dns/labelsequence.h @@ -58,7 +58,7 @@ public: /// //@{ /// Wildcard label ("*") - static const LabelSequence& WILDCARD_LABEL(); + static const LabelSequence& WILDCARD(); //@} /// \brief Constructs a LabelSequence for the given name @@ -419,7 +419,7 @@ std::ostream& operator<<(std::ostream& os, const LabelSequence& label_sequence); inline const LabelSequence& -LabelSequence::WILDCARD_LABEL() { +LabelSequence::WILDCARD() { static const uint8_t wildcard_buf[4] = { 0x01, 0x00, 0x01, '*' }; static const LabelSequence wild_ls(wildcard_buf); return (wild_ls); diff --git a/src/lib/dns/tests/labelsequence_unittest.cc b/src/lib/dns/tests/labelsequence_unittest.cc index 06092c008e..b7463b2460 100644 --- a/src/lib/dns/tests/labelsequence_unittest.cc +++ b/src/lib/dns/tests/labelsequence_unittest.cc @@ -1176,8 +1176,8 @@ TEST_F(ExtendableLabelSequenceTest, extendBadData) { // Check the static fixed 'wildcard' LabelSequence TEST(WildCardLabelSequence, wildcard) { - ASSERT_FALSE(LabelSequence::WILDCARD_LABEL().isAbsolute()); - ASSERT_EQ("*", LabelSequence::WILDCARD_LABEL().toText()); + ASSERT_FALSE(LabelSequence::WILDCARD().isAbsolute()); + ASSERT_EQ("*", LabelSequence::WILDCARD().toText()); } } From 3c03d322a1845fd162f8e3bb83e403f3a1977847 Mon Sep 17 00:00:00 2001 From: Mukund Sivaraman Date: Tue, 11 Sep 2012 18:30:06 +0530 Subject: [PATCH 145/199] [2150] Allow DomainTree::find() to start at a lower level Note that if a previous find() results in a PARTIALMATCH, the next find() (when passed a populated node chain and a non-absolute label sequence) starts at the down-node of the partial match's top of chain (deepest matched node). The caller must pass the correctly formed non-absolute label sequence for that sub-tree-of-tree. --- src/lib/datasrc/memory/domaintree.h | 25 ++++++++++---- .../memory/tests/domaintree_unittest.cc | 34 +++++++++++++++++++ 2 files changed, 53 insertions(+), 6 deletions(-) diff --git a/src/lib/datasrc/memory/domaintree.h b/src/lib/datasrc/memory/domaintree.h index db2f7e6871..b29f10feac 100644 --- a/src/lib/datasrc/memory/domaintree.h +++ b/src/lib/datasrc/memory/domaintree.h @@ -1153,9 +1153,11 @@ public: /// Another special feature of this version is the ability to record /// more detailed information regarding the search result. /// - /// This information will be returned via the \c node_path parameter, - /// which is an object of class \c DomainTreeNodeChain. - /// The passed parameter must be empty. + /// This information will be returned via the \c node_path + /// parameter, which is an object of class \c DomainTreeNodeChain. + /// The passed parameter must be empty if the label sequence is + /// absolute. If the label sequence is not absolute, then find() + /// will begin from the top of the node chain. /// /// On success, the node sequence stored in \c node_path will contain all /// the ancestor nodes from the found node towards the root. @@ -1462,12 +1464,23 @@ DomainTree::find(const isc::dns::LabelSequence& target_labels_orig, bool (*callback)(const DomainTreeNode&, CBARG), CBARG callback_arg) const { - if (!node_path.isEmpty()) { + if (node_path.isEmpty() ^ target_labels_orig.isAbsolute()) { isc_throw(isc::BadValue, - "DomainTree::find is given a non empty chain"); + "DomainTree::find() is given mismatched node chain" + " and label sequence"); + } + + DomainTreeNode* node; + + if (!node_path.isEmpty()) { + // Get the top node in the node chain + node = const_cast*>(node_path.top()); + // Start searching from its down pointer + node = node->getDown(); + } else { + node = root_.get(); } - DomainTreeNode* node = root_.get(); Result ret = NOTFOUND; dns::LabelSequence target_labels(target_labels_orig); diff --git a/src/lib/datasrc/memory/tests/domaintree_unittest.cc b/src/lib/datasrc/memory/tests/domaintree_unittest.cc index 3d1ab56cb9..9229aed2dc 100644 --- a/src/lib/datasrc/memory/tests/domaintree_unittest.cc +++ b/src/lib/datasrc/memory/tests/domaintree_unittest.cc @@ -454,6 +454,40 @@ TEST_F(DomainTreeTest, callbackLabelSequence) { performCallbackTest(dtree, mem_sgmt_, ls1, ls2); } +TEST_F(DomainTreeTest, findInSubTree) { + // For the version that takes a node chain, the chain must be empty. + DomainTreeNodeChain chain; + bool flag; + + // First, find a sub-tree node + const Name n1("w.y.d.e.f"); + const LabelSequence ls1(n1); + DomainTree::Result result = + dtree_expose_empty_node.find(ls1, &cdtnode, chain, + testCallback, &flag); + EXPECT_EQ(DomainTree::EXACTMATCH, result); + EXPECT_EQ(n1, chain.getAbsoluteName()); + + // Searching for an absolute label sequence when chain is already + // populated should throw. + const Name n2a("o"); + const LabelSequence ls2a(n2a); + EXPECT_THROW(dtree_expose_empty_node.find(ls2a, &cdtnode, chain, + testCallback, &flag), + isc::BadValue); + + // Now, find "o.w.y.d.e.f." by right-stripping the "w.y.d.e.f." + // suffix. + const Name n2("o.w.y.d.e.f"); + LabelSequence ls2(n2); + ls2.stripRight(6); + + result = dtree_expose_empty_node.find(ls2, &cdtnode, chain, + testCallback, &flag); + EXPECT_EQ(DomainTree::EXACTMATCH, result); + EXPECT_EQ(n2, chain.getAbsoluteName()); +} + TEST_F(DomainTreeTest, chainLevel) { TestDomainTreeNodeChain chain; From d8dd6f7cb2a0118d692c924d5652d5ec84906117 Mon Sep 17 00:00:00 2001 From: Marcin Siodelski Date: Tue, 11 Sep 2012 16:17:37 +0200 Subject: [PATCH 146/199] [2231] Implemented sub-second timeouts for IfaceMgr::receiveX functions. --- src/lib/dhcp/iface_mgr.cc | 20 ++--- src/lib/dhcp/iface_mgr.h | 14 +-- src/lib/dhcp/tests/iface_mgr_unittest.cc | 106 +++++++++++++++++++++++ 3 files changed, 124 insertions(+), 16 deletions(-) diff --git a/src/lib/dhcp/iface_mgr.cc b/src/lib/dhcp/iface_mgr.cc index 90f6353622..cff9f12b2d 100644 --- a/src/lib/dhcp/iface_mgr.cc +++ b/src/lib/dhcp/iface_mgr.cc @@ -1,4 +1,4 @@ -// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC") +// Copyright (C) 2011-2012 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 @@ -785,7 +785,7 @@ IfaceMgr::send(const Pkt4Ptr& pkt) boost::shared_ptr -IfaceMgr::receive4(uint32_t timeout) { +IfaceMgr::receive4(uint32_t timeout_sec, uint32_t timeout_usec) { const SocketInfo* candidate = 0; IfaceCollection::const_iterator iface; @@ -825,13 +825,12 @@ IfaceMgr::receive4(uint32_t timeout) { names << session_socket_ << "(session)"; } - /// @todo: implement sub-second precision one day struct timeval select_timeout; - select_timeout.tv_sec = timeout; - select_timeout.tv_usec = 0; + select_timeout.tv_sec = timeout_sec; + select_timeout.tv_usec = timeout_usec; cout << "Trying to receive data on sockets: " << names.str() - << ". Timeout is " << timeout << " seconds." << endl; + << ". Timeout is " << timeout_sec << " seconds." << endl; int result = select(maxfd + 1, &sockets, NULL, NULL, &select_timeout); cout << "select returned " << result << endl; @@ -953,7 +952,7 @@ IfaceMgr::receive4(uint32_t timeout) { return (pkt); } -Pkt6Ptr IfaceMgr::receive6(uint32_t timeout) { +Pkt6Ptr IfaceMgr::receive6(uint32_t timeout_sec, uint32_t timeout_usec) { const SocketInfo* candidate = 0; fd_set sockets; @@ -994,12 +993,11 @@ Pkt6Ptr IfaceMgr::receive6(uint32_t timeout) { } cout << "Trying to receive data on sockets:" << names.str() - << ".Timeout is " << timeout << " seconds." << endl; + << ".Timeout is " << timeout_sec << " seconds." << endl; - /// @todo: implement sub-second precision one day struct timeval select_timeout; - select_timeout.tv_sec = timeout; - select_timeout.tv_usec = 0; + select_timeout.tv_sec = timeout_sec; + select_timeout.tv_usec = timeout_usec; int result = select(maxfd + 1, &sockets, NULL, NULL, &select_timeout); diff --git a/src/lib/dhcp/iface_mgr.h b/src/lib/dhcp/iface_mgr.h index 36ad0eada2..cbc254b65d 100644 --- a/src/lib/dhcp/iface_mgr.h +++ b/src/lib/dhcp/iface_mgr.h @@ -1,4 +1,4 @@ -// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC") +// Copyright (C) 2011-2012 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 @@ -345,10 +345,12 @@ public: /// to not wait infinitely, but rather do something useful /// (e.g. remove expired leases) /// - /// @param timeout specifies timeout (in seconds) + /// @param timeout_sec specifies integral part of the timeout (in seconds) + /// @param timeout_usec specifies fractional part of the timeout + /// (in microseconds) /// /// @return Pkt6 object representing received packet (or NULL) - Pkt6Ptr receive6(uint32_t timeout); + Pkt6Ptr receive6(uint32_t timeout_sec, uint32_t timeout_usec = 0); /// @brief Tries to receive IPv4 packet over open IPv4 sockets. /// @@ -356,10 +358,12 @@ public: /// If reception is successful and all information about its sender /// are obtained, Pkt4 object is created and returned. /// - /// @param timeout specifies timeout (in seconds) + /// @param timeout_sec specifies integral part of the timeout (in seconds) + /// @param timeout_usec specifies fractional part of the timeout + /// (in microseconds) /// /// @return Pkt4 object representing received packet (or NULL) - Pkt4Ptr receive4(uint32_t timeout); + Pkt4Ptr receive4(uint32_t timeout_sec, uint32_t timeout_usec = 0); /// Opens UDP/IP socket and binds it to address, interface and port. /// diff --git a/src/lib/dhcp/tests/iface_mgr_unittest.cc b/src/lib/dhcp/tests/iface_mgr_unittest.cc index 5562551a0e..3a05651529 100644 --- a/src/lib/dhcp/tests/iface_mgr_unittest.cc +++ b/src/lib/dhcp/tests/iface_mgr_unittest.cc @@ -217,6 +217,112 @@ TEST_F(IfaceMgrTest, getIface) { } +TEST_F(IfaceMgrTest, receiveTimeout6) { + std::cout << "Testing DHCPv6 packet reception timeouts." + << " Test will block for a few seconds when waiting" + << " for timeout to occur." << std::endl; + + boost::scoped_ptr ifacemgr(new NakedIfaceMgr()); + // Open socket on the lo interface. + IOAddress loAddr("::1"); + int socket1 = 0; + ASSERT_NO_THROW( + socket1 = ifacemgr->openSocket(LOOPBACK, loAddr, 10547) + ); + // Socket is open if its descriptor is greater than zero. + ASSERT_GT(socket1, 0); + + // Time when call to IfaceMgr::receive6() started. + timeval start_time; + // Time when call to IfaceMgr::receive6() ended. + timeval stop_time; + + // Remember when we call receive6(). + gettimeofday(&start_time, NULL); + // Call receive with timeout of 1s + 1000us. + Pkt6Ptr pkt = ifacemgr->receive6(1, 1000); + // Remember when call to receive6() ended. + gettimeofday(&stop_time, NULL); + // We did not send a packet to lo interface so we expect that + // nothing has been received and timeout has been reached. + ASSERT_FALSE(pkt); + // Calculate duration of call to receive6(). + stop_time.tv_sec -= start_time.tv_sec; + stop_time.tv_usec -= start_time.tv_usec; + // Duration should be equal or greater than timeout specified + // for the receive6() call. + EXPECT_EQ(stop_time.tv_sec, 1); + EXPECT_GT(stop_time.tv_usec, 1000); + + // Test timeout shorter than 1s. + gettimeofday(&start_time, NULL); + pkt = ifacemgr->receive6(0, 500); + gettimeofday(&stop_time, NULL); + ASSERT_FALSE(pkt); + stop_time.tv_sec -= start_time.tv_sec; + stop_time.tv_usec -= start_time.tv_usec; + // The timeout has been set to 500us. Even though the way we measure + // duration of receive6() may result in durations slightly longer than + // timeout it is safe to assume that measured value will not exceed 1s + // when timeout is only 500us. If it exceeds, this is an error and + // should be investigated. + EXPECT_EQ(0, stop_time.tv_sec); + EXPECT_GT(stop_time.tv_usec, 500); +} + +TEST_F(IfaceMgrTest, receiveTimeout4) { + std::cout << "Testing DHCPv6 packet reception timeouts." + << " Test will block for a few seconds when waiting" + << " for timeout to occur." << std::endl; + + boost::scoped_ptr ifacemgr(new NakedIfaceMgr()); + // Open socket on the lo interface. + IOAddress loAddr("127.0.0.1"); + int socket1 = 0; + ASSERT_NO_THROW( + socket1 = ifacemgr->openSocket(LOOPBACK, loAddr, 10067) + ); + // Socket is open if its descriptor is greater than zero. + ASSERT_GT(socket1, 0); + + // Time when call to IfaceMgr::receive4() started. + timeval start_time; + // Time when call to IfaceMgr::receive4() ended. + timeval stop_time; + + // Remember when we call receive4(). + gettimeofday(&start_time, NULL); + // Call receive with timeout of 2s + 150us. + Pkt4Ptr pkt = ifacemgr->receive4(2, 150); + // Remember when call to receive4() ended. + gettimeofday(&stop_time, NULL); + // We did not send a packet to lo interface so we expect that + // nothing has been received and timeout has been reached. + ASSERT_FALSE(pkt); + // Calculate duration of call to receive4(). + stop_time.tv_sec -= start_time.tv_sec; + stop_time.tv_usec -= start_time.tv_usec; + // Duration should be equal or greater than timeout specified + // for the receive4() call. + EXPECT_EQ(stop_time.tv_sec, 2); + EXPECT_GT(stop_time.tv_usec, 150); + + // Test timeout shorter than 1s. + gettimeofday(&start_time, NULL); + pkt = ifacemgr->receive4(0, 350); + gettimeofday(&stop_time, NULL); + ASSERT_FALSE(pkt); + stop_time.tv_sec -= start_time.tv_sec; + stop_time.tv_usec -= start_time.tv_usec; + // The timeout has been set to 350us. Even though the way we measure + // duration of receive4() may result in durations slightly longer than + // timeout it is safe to assume that measured value will not exceed 1s + // when timeout is only 350us. If it exceeds, this is an error and + // should be investigated. + EXPECT_EQ(0, stop_time.tv_sec); + EXPECT_GT(stop_time.tv_usec, 350); +} + TEST_F(IfaceMgrTest, sockets6) { // testing socket operation in a portable way is tricky // without interface detection implemented From b0414cf54bb6c446c98c3e791d3212b02ff50c8f Mon Sep 17 00:00:00 2001 From: Marcin Siodelski Date: Tue, 11 Sep 2012 17:25:52 +0200 Subject: [PATCH 147/199] [2231] Added sanity check for microsecond timeout. --- src/lib/dhcp/iface_mgr.cc | 11 ++++++++++- src/lib/dhcp/iface_mgr.h | 2 ++ src/lib/dhcp/tests/iface_mgr_unittest.cc | 18 ++++++++++++++---- 3 files changed, 26 insertions(+), 5 deletions(-) diff --git a/src/lib/dhcp/iface_mgr.cc b/src/lib/dhcp/iface_mgr.cc index cff9f12b2d..0c99647e27 100644 --- a/src/lib/dhcp/iface_mgr.cc +++ b/src/lib/dhcp/iface_mgr.cc @@ -786,7 +786,11 @@ IfaceMgr::send(const Pkt4Ptr& pkt) boost::shared_ptr IfaceMgr::receive4(uint32_t timeout_sec, uint32_t timeout_usec) { - + // Sanity check for microsecond timeout. + if (timeout_usec >= 1000000) { + isc_throw(BadValue, "fractional timeout must be shorter than" + " one million microseconds"); + } const SocketInfo* candidate = 0; IfaceCollection::const_iterator iface; fd_set sockets; @@ -953,6 +957,11 @@ IfaceMgr::receive4(uint32_t timeout_sec, uint32_t timeout_usec) { } Pkt6Ptr IfaceMgr::receive6(uint32_t timeout_sec, uint32_t timeout_usec) { + // Sanity check for microsecond timeout. + if (timeout_usec >= 1000000) { + isc_throw(BadValue, "fractional timeout must be shorter than" + " one million microseconds"); + } const SocketInfo* candidate = 0; fd_set sockets; diff --git a/src/lib/dhcp/iface_mgr.h b/src/lib/dhcp/iface_mgr.h index cbc254b65d..922c701733 100644 --- a/src/lib/dhcp/iface_mgr.h +++ b/src/lib/dhcp/iface_mgr.h @@ -349,6 +349,7 @@ public: /// @param timeout_usec specifies fractional part of the timeout /// (in microseconds) /// + /// @throw isc::BadValue if timeout_usec is greater than one million /// @return Pkt6 object representing received packet (or NULL) Pkt6Ptr receive6(uint32_t timeout_sec, uint32_t timeout_usec = 0); @@ -362,6 +363,7 @@ public: /// @param timeout_usec specifies fractional part of the timeout /// (in microseconds) /// + /// @throw isc::BadValue if timeout_usec is greater than one million /// @return Pkt4 object representing received packet (or NULL) Pkt4Ptr receive4(uint32_t timeout_sec, uint32_t timeout_usec = 0); diff --git a/src/lib/dhcp/tests/iface_mgr_unittest.cc b/src/lib/dhcp/tests/iface_mgr_unittest.cc index 3a05651529..3076658a6f 100644 --- a/src/lib/dhcp/tests/iface_mgr_unittest.cc +++ b/src/lib/dhcp/tests/iface_mgr_unittest.cc @@ -240,7 +240,8 @@ TEST_F(IfaceMgrTest, receiveTimeout6) { // Remember when we call receive6(). gettimeofday(&start_time, NULL); // Call receive with timeout of 1s + 1000us. - Pkt6Ptr pkt = ifacemgr->receive6(1, 1000); + Pkt6Ptr pkt; + ASSERT_NO_THROW(pkt = ifacemgr->receive6(1, 1000)); // Remember when call to receive6() ended. gettimeofday(&stop_time, NULL); // We did not send a packet to lo interface so we expect that @@ -256,7 +257,7 @@ TEST_F(IfaceMgrTest, receiveTimeout6) { // Test timeout shorter than 1s. gettimeofday(&start_time, NULL); - pkt = ifacemgr->receive6(0, 500); + ASSERT_NO_THROW(pkt = ifacemgr->receive6(0, 500)); gettimeofday(&stop_time, NULL); ASSERT_FALSE(pkt); stop_time.tv_sec -= start_time.tv_sec; @@ -268,6 +269,10 @@ TEST_F(IfaceMgrTest, receiveTimeout6) { // should be investigated. EXPECT_EQ(0, stop_time.tv_sec); EXPECT_GT(stop_time.tv_usec, 500); + + // Test with invalid fractional timeout values. + EXPECT_THROW(ifacemgr->receive6(0, 1000000), isc::BadValue); + EXPECT_THROW(ifacemgr->receive6(1, 1000010), isc::BadValue); } TEST_F(IfaceMgrTest, receiveTimeout4) { @@ -293,7 +298,8 @@ TEST_F(IfaceMgrTest, receiveTimeout4) { // Remember when we call receive4(). gettimeofday(&start_time, NULL); // Call receive with timeout of 2s + 150us. - Pkt4Ptr pkt = ifacemgr->receive4(2, 150); + Pkt4Ptr pkt; + ASSERT_NO_THROW(pkt = ifacemgr->receive4(2, 150)); // Remember when call to receive4() ended. gettimeofday(&stop_time, NULL); // We did not send a packet to lo interface so we expect that @@ -309,7 +315,7 @@ TEST_F(IfaceMgrTest, receiveTimeout4) { // Test timeout shorter than 1s. gettimeofday(&start_time, NULL); - pkt = ifacemgr->receive4(0, 350); + ASSERT_NO_THROW(pkt = ifacemgr->receive4(0, 350)); gettimeofday(&stop_time, NULL); ASSERT_FALSE(pkt); stop_time.tv_sec -= start_time.tv_sec; @@ -321,6 +327,10 @@ TEST_F(IfaceMgrTest, receiveTimeout4) { // should be investigated. EXPECT_EQ(0, stop_time.tv_sec); EXPECT_GT(stop_time.tv_usec, 350); + + // Test with invalid fractional timeout values. + EXPECT_THROW(ifacemgr->receive6(0, 1000000), isc::BadValue); + EXPECT_THROW(ifacemgr->receive6(2, 1000005), isc::BadValue); } TEST_F(IfaceMgrTest, sockets6) { From 53bf6472668ff2fcd1b192f322b83301ea66abe4 Mon Sep 17 00:00:00 2001 From: Stephen Morris Date: Wed, 12 Sep 2012 11:25:33 +0100 Subject: [PATCH 148/199] [2230] Updates to the text in the perfdhcp part of the guide --- tests/tools/dhcp-ubench/dhcp-perf-guide.html | 295 ++++++++++--------- tests/tools/dhcp-ubench/dhcp-perf-guide.xml | 243 +++++++-------- 2 files changed, 276 insertions(+), 262 deletions(-) diff --git a/tests/tools/dhcp-ubench/dhcp-perf-guide.html b/tests/tools/dhcp-ubench/dhcp-perf-guide.html index 27da0b1c0d..f610476c70 100644 --- a/tests/tools/dhcp-ubench/dhcp-perf-guide.html +++ b/tests/tools/dhcp-ubench/dhcp-perf-guide.html @@ -1,25 +1,23 @@ - - - DHCP Performance Guide

DHCP Performance Guide

Tomasz Mrugalski

Marcin Siodelski

This is a companion document for BIND 10 version - 20120712.

Abstract

BIND 10 is a framework that features Domain Name System +DHCP Performance Guide

DHCP Performance Guide

Tomasz Mrugalski

Marcin Siodelski

This is a companion document for BIND 10 version + 20120817.

Abstract

BIND 10 is a framework that features Domain Name System (DNS) suite and Dynamic Host Configuration Protocol (DHCP) servers with development managed by Internet Systems Consortium (ISC). This document describes various aspects of DHCP performance, measurements and tuning. It covers BIND 10 DHCP (codename Kea), existing ISC DHCP4 software, perfdhcp (a DHCP performance - measurement tool) and other related topics.


Preface

Table of Contents

Acknowledgements

Acknowledgements

ISC would like to acknowledge generous support for + measurement tool) and other related topics.


Preface

Table of Contents

1. Acknowledgements

1. Acknowledgements

ISC would like to acknowledge generous support for BIND 10 development of DHCPv4 and DHCPv6 components provided - by Comcast.

Chapter 1. Introduction

+ by Comcast.

Chapter 1. Introduction

This document is in its early stages of development. It is expected to grow significantly in a near future. It will cover topics like database backend perfomance measurements, pros an cons of various optimization techniques and tools. -

Chapter 2. ISC DHCP 4.x

+

Chapter 2. ISC DHCP 4.x

TODO: Write something about ISC DHCP4 here. -

Chapter 3. Kea

-

Backend performance evaluation

+

3.1. Backend performance evaluation

Kea will support several different database backends, using both popular databases (like MySQL or SQLite) and custom-developed solutions (like in-memory database). BIND 10 @@ -64,7 +62,7 @@

The framework attempts to do the same amount of operations for every backend thus allowing fair complarison between them. -

MySQL backend

MySQL backend requires MySQL client development libraries. It uses +

3.2. MySQL backend

MySQL backend requires MySQL client development libraries. It uses mysql_config tool (that works similar to pkg-config) to discover required compilation and linking options. To install required packages on Ubuntu, use the following command: @@ -113,13 +111,13 @@ Possible command-line parameters: -c yes|no - should compiled statements be used (MySQL only)

-

MySQL tweaks

One parameter that has huge impact on performance is a a backend engine. +

3.2.1. MySQL tweaks

One parameter that has huge impact on performance is a a backend engine. You can get a list of engines of your MySQL implementation by using

> show engines;

in your mysql client. Two notable engines are MyISAM and InnoDB. mysql_ubench will - use MyISAM for synchronous mode and InnoDB for asynchronous.

SQLite-ubench

SQLite backend requires both sqlite3 development and run-time package. Their + use MyISAM for synchronous mode and InnoDB for asynchronous.

3.3. SQLite-ubench

SQLite backend requires both sqlite3 development and run-time package. Their names may vary from system to system, but on Ubuntu 12.04 they are called sqlite3 libsqlite3-dev. To install them, use the following command: @@ -134,7 +132,7 @@ Possible command-line parameters:

> ./sqlite_ubench

or

> ./sqlite_ubench > results-sqlite.txt

-

SQLite tweaks

To modify default sqlite_ubench parameters, command line +

3.3.1. SQLite tweaks

To modify default sqlite_ubench parameters, command line switches can be used. Currently supported parameters are (default values specified in brackets):

  1. -f filename - name of the database file ("sqlite.db")

  2. -n num - number of iterations (100)

  3. -s yes|no - should the operations be performend in synchronous (yes) @@ -145,13 +143,13 @@ Possible command-line parameters: turned to several modes of operation. Its value can be modified in SQLite_uBenchmark::connect(). See http://www.sqlite.org/pragma.html#pragma_journal_mode for - detailed explanantion.

memfile-ubench

Memfile backend is custom developed prototype backend that + detailed explanantion.

3.4. memfile-ubench

Memfile backend is custom developed prototype backend that somewhat mimics operation of ISC DHCP4. It uses in-memory storage using standard C++ and boost mechanisms (std::map and boost::shared_ptr<>). All database changes are also written to a lease file. That file is strictly write-only. This approach takes advantage of the fact that simple append is faster - than edition with potential whole file relocation.

memfile tweaks

To modify default memfile_ubench parameters, command line + than edition with potential whole file relocation.

3.4.1. memfile tweaks

To modify default memfile_ubench parameters, command line switches can be used. Currently supported parameters are (default values specified in brackets):

  1. -f filename - name of the database file ("dhcpd.leases")

  2. -n num - number of iterations (100)

  3. -s yes|no - should the operations be performend in synchronous (yes) @@ -159,7 +157,7 @@ Possible command-line parameters:

    memfile can run in asynchronous or synchronous mode. This mode can be controlled by using sync parameter. It uses fflush() and fsync() in synchronous mode to make sure that - data is not buffered and physically stored on disk.

Performance measurements

This section contains sample results for backend performance measurements, + data is not buffered and physically stored on disk.

3.5. Performance measurements

This section contains sample results for backend performance measurements, taken using microbenchmarks. Tests were conducted on reasonably powerful machine:

 CPU: Quad-core Intel(R) Core(TM) i7-2600K CPU @ 3.40GHz (8 logical cores)
@@ -172,13 +170,13 @@ SQLite version: 3.7.9sourceid version is 2011-11-01 00:52:41 c7c6050ef060877ebe7
       asynchronous. As those modes offer radically different
       performances, synchronous mode was conducted for 1000 (one
       thousand) repetitions and asynchronous mode was conducted for
-      100000 (hundred thousand) repetitions.

Table 3.1. Synchronous results

BackendOperationsCreateSearchUpdateDeleteAverage
MySQL100031.603978s 0.116612s27.964191s27.695209s21.844998s
SQLite100061.421356s 0.033283s59.476638s56.034150s44.241357s
memfile100041.711886s 0.000724s42.267578s42.169679s31.537467s

Following parameters were measured for asynchronous mode. + 100000 (hundred thousand) repetitions.

Table 3.1. Synchronous results

BackendOperationsCreateSearchUpdateDeleteAverage
MySQL100031.603978s 0.116612s27.964191s27.695209s21.844998s
SQLite100061.421356s 0.033283s59.476638s56.034150s44.241357s
memfile100041.711886s 0.000724s42.267578s42.169679s31.537467s

Following parameters were measured for asynchronous mode. MySQL and SQLite were run with 100 thousand repetitions. Memfile - was run for 1 million repetitions due to much larger performance.

Table 3.2. Asynchronous results

BackendOperationsCreate [s]Search [s]Update [s]Delete [s]Average [s]
MySQL10000010.584842s10.386402s10.062384s 8.890197s 9.980956s
SQLite100000 3.710356s 3.159129s 2.865354s 2.439406s 3.043561s
memfile1000000 (sic!) 6.084131s 0.862667s 6.018585s 5.146704s 4.528022s

Presented performance results can be computed into operations per second metrics. + was run for 1 million repetitions due to much larger performance.

Table 3.2. Asynchronous results

BackendOperationsCreate [s]Search [s]Update [s]Delete [s]Average [s]
MySQL10000010.584842s10.386402s10.062384s 8.890197s 9.980956s
SQLite100000 3.710356s 3.159129s 2.865354s 2.439406s 3.043561s
memfile1000000 (sic!) 6.084131s 0.862667s 6.018585s 5.146704s 4.528022s

Presented performance results can be computed into operations per second metrics. It should be noted that due to large differences between various operations (sometime over 3 orders of magnitude), it is difficult to create a simple, readable chart with - that data.

Table 3.3. Estimated performance

BackendCreate [oper/s]Search [oper/s]Update [oper/s]Delete [oper/s]Average [oper/s]
MySQL (async)9447.479627.979938.0011248.3410065.45
SQLite (async)26951.5931654.2934899.7040993.5933624.79
memfile (async)164362.011159195.84166152.01194299.11421002.24
MySQL (sync)31.648575.4535.7636.112169.74
SQLite (sync)16.2820045.3716.8117.857524.08
memfile (sync)23.971381215.4723.6623.71345321.70

Performance measurements

Graphical representation of the performance results - presented in table Table 3.3, “Estimated performance”.

Possible further optimizations

+ that data.

Table 3.3. Estimated performance

BackendCreate [oper/s]Search [oper/s]Update [oper/s]Delete [oper/s]Average [oper/s]
MySQL (async)9447.479627.979938.0011248.3410065.45
SQLite (async)26951.5931654.2934899.7040993.5933624.79
memfile (async)164362.011159195.84166152.01194299.11421002.24
MySQL (sync)31.648575.4535.7636.112169.74
SQLite (sync)16.2820045.3716.8117.857524.08
memfile (sync)23.971381215.4723.6623.71345321.70

Performance measurements

Graphical representation of the performance results + presented in table Table 3.3, “Estimated performance”.

3.6. Possible further optimizations

For debugging purposes the code was compiled with -g -O0 flags. While majority of the time was spent in backend functions (that was probably compiled with -O2 flags), the @@ -196,54 +194,51 @@ SQLite version: 3.7.9sourceid version is 2011-11-01 00:52:41 c7c6050ef060877ebe7 Multi-threaded or multi-process benchmark may be considered in the future. It may be somewhat difficult as only some backends support concurrent access. -

Chapter 4. perfdhcp

Purpose

- There is a growing need to evaluate performance of DHCP servers in - different traffic conditions to understand their bottle necks. - This helps to elimante bugs in existing DHCP software as well - as make informed decisions regarding new DHCP software designs - to significantly improve its performance. The perfdhcp tool has - been created to fill the gap in performance measurement capabilities - mostly. However, the number of implemented features and parameters - exposed to the user make this tool useful for functional testing as - well. -

Key features

- The perfdhcp exposes the number of command line parameters to - control DHCP message exchanges. Currently they fall back to +

Chapter 4. perfdhcp

4.1. Purpose

+ Evaluation of the performance of a DHCP server requires that it + be tested under varying traffic loads. perfdhcp is a testing + tool with the capability to create traffic loads + and generate statistics from the results. Additional features, + such as the ability to send customised DHCP packets, allow it to + be used in a wide range of functional testing. +

4.2. Key features

+ perfdhcp has a number of command line switches to + control DHCP message exchanges. Currently they fall into the following categories: -

  1. +

    • Rate control - control how many DHCP exchanges - are initiated within a period of time. Tool can also simulate - best effort conditions attempting to initiate as many DHCP - packet exchanges within a unit of time as possible. + are initiated within a period of time. The tool can also simulate + best effort conditions by attempting to initiate as many DHCP + packet exchanges as possible within a unit of time.

    • - Test exit specifiers - control the conditions when test - completes including number of initiated exchanges, test period or - maximum number of dropped packets. + Test exit specifiers - control the conditions for test + completion, including the number of initiated exchanges, + the test period orthe maximum number of dropped packets.

    • Packet templates - specify files containing packet templates that - are used by perfdhcp to create custom DHCP messages instead of - default. Tool also allows to specify number of values indicating - offsets of variable values within a packet that are modified in - flight by the tool. + are used by perfdhcp to create custom DHCP messages. The tool + allows the specification of a number of values indicating + offsets of values within a packet that are set by the tool.

    • - Reporting - for each test produce the set of performance data - including achieved packet exchange rate (server performance). - There is also a number of diagnostic selectors available that - enable periodic (intermediate) reporting, packet timestamps - printing and detailed information about perfdhcp internal + Reporting - for each test produce a set of performance data + including the achieved packet exchange rate (server performance). + There are a number of diagnostic selectors available that + enable periodic (intermediate) reporting, printing of packet timestamps, + and the listing of detailed information about internal perfdhcp states (for debugging).

    • - Different mode of operations - specify DHCP version used - (v4 or v6), 2-way or 4-way exchanges, use Rapid Commit option - for DHCPv6. + Different mode of operations - specify the DHCP protocol used + (v4 or v6), two-way or four-way exchanges, use of the + Rapid Commit option for DHCPv6.

    • - IP layer options - specify local/remote address, local interface + IP layer options - specify the local/remote address, local interface and local port to be used for communication with DHCP server. -

-

Command line options

- The following command line options may be used with perfdhcp tool. - This summary also presents its possible exit codes as well as - error counters printed along when test is complete: +

+

4.3. Command line options

+ The following "help" output from the tool describes the + command line switches. This summary also lists the tool's + possible exit codes as well as describing the + error counters printed when the test is complete:

$ ./perfdhcp -h
 perfdhcp [-hv] [-4|-6] [-r<rate>] [-t<report>] [-R<range>] [-b<base>]
     [-n<num-request>] [-p<test-period>] [-d<drop-time>] [-D<max-drop>]
@@ -367,47 +362,51 @@ The exit status is:
   exchanges are not successfully completed.
 
         

-

Starting perfdhcp

- In order to run performance test at least two separate systems - have to be installed: client and server. The first one has to have - perfdhcp tool installed, the latter has to have DHCP server - running (v4 or v6). If only single system is available the client +

4.4. Starting perfdhcp

+ In order to run a performance test, at least two separate systems + have to be installed: client and server. The first one must have + perfdhcp installed, and the latter must be running the DHCP server + (either v4 or v6). If only single system is available the client and server can be run on virtual machines (running on the same - phisical system) but in this case performance data may be heavily - impacted by the performance of VMs. + physical system) but in this case performance data may be heavily + impacted by the overhead involved in running such the virtual + machines.

- The DHCP operates on low port numbers (67 for DHCPv4 relays and - 547 for DHCPv6). Running perfdhcp with non-root priviliges will + Currently, perfdhcp is seen from the server perspective as relay agent. + This simplifies its implementation: specifically there is no need to + receive traffic sent to braodcast addresses. However, it does impose + a requirement that the IPv4 + address has to be set manually on the interface that will be used to + communicate with the server. For example, if the DHCPv4 server is listening + on the interface connected to the 172.16.1.0 subnet, the interface on client + machine has to have network address assigned from the same subnet, e.g. +

#ifconfig eth3 172.16.1.2. netmask 255.255.255.0 up

+

+ As DHCP uses low port numbers (67 for DHCPv4 relays and + 547 for DHCPv6), running perfdhcp with non-root privileges will usually result in the error message similar to this: +

$./perfdhcp -4 -l eth3 -r 100 all
 Error running perfdhcp: Failed to bind socket 3 to 172.16.1.2/port=67
         

- perfdhcp has the '-L' command line option that - imposes use of custom local port. Thus the following command - line will work: + + The '-L' command line switch allows the use of a custom local port. + However, although the following command line will work: +

$./perfdhcp -4 -l eth3 -r 100 -L 10067 all

- but in the standard configuration no responses will be received - from the ISC DHCP server because server responds to default relay - port 67. - Alternative way to overcome this issue is to run perfdhcp as root. -

- Currently, perfdhcp is seen from the server perspective as relay agent. - This simplifies its implementation (specifically there is no need to - receive traffic sent to braodcast addresses). This imposes that IPv4 - address has to be set manually on the interface that will be used to - communicate with the server. For example, if DHCPv4 server is listening - on the interface connected to 172.16.1.0 subnet, interface on client - machine has to have network address assigned from the same subnet - on one of its interfaces connected to this subnet: -

#ifconfig eth3 172.16.1.2. netmask 255.255.255.0 up

-

perfdhcp command line examples

- In this section the perfdhcp command line examples + + in the standard configuration no responses will be received + from the DHCP server because the server responds to default relay + port 67. A way to overcome this issue is to run + perfdhcp as root. +

4.5. perfdhcp command line examples

+ In this section, a number of perfdhcp command line examples are presented as a quick start guide for new users. For the detailed list of command line options refer to - the section called “Command line options”. -

Example: basic usage

- If server is listening on interface with IPv4 address 172.16.1.1 - the simpliest perfdhcp command line will look like this: + Section 4.3, “Command line options”. +

4.5.1. Example: basic usage

+ If server is listening on interface with IPv4 address 172.16.1.1, + the simplest perfdhcp command line will look like:

#./perfdhcp 172.16.1.1
 ***Rate statistics***
 Rate: 206.345
@@ -436,15 +435,13 @@ max delay: 258.634 ms
 std deviation: 56.936 ms
 collected packets: 0
           

- In this case perfdhcp will use remote address 172.16.1.1 as a - destination address and will use suitable local interface for - communication. Since, no rate control parameters have been specified - it will be initiating DHCP exchanges with the maximum possible - rate (it will try to initiate maximum number of exchanges per - second and count number of completed exchanged). Due to server's - performance constraints, many DHCP packets sent to server are likely - to be dropped. The performance test will be running until it is - not interrupted by the user (with ^C). + Here, perfdhcp uses remote address 172.16.1.1 as a + destination address and will use a suitable local interface for + communication. Since, no rate control parameters have been specified, + it will initiate DHCP exchanges at the maximum possible rate. Due to the server's + performance limitation, it is likely that many of the packets will be dropped. + The performance test will continue running until it is + interrupted by the user (with ^C).

The default performance statistics reported by perfdhcp have the following meaning: @@ -453,40 +450,44 @@ collected packets: 0 completed within a second.

  • sent packets - total number of DHCP packets of a specific type sent to the server.

  • received packets - total number of DHCP packets of specific type received from the server.

  • drops - number of dropped packets for the - particular exchange. Number of dropped packets is calculated as - a difference between number of sent packets and number of - response packets received from the server. It is likely that - server sent the reponse but perfdhcp execution had ended before - reponse arrived. In such case this packet will be assumed - dropped.

  • orphans - number of packets that have been + particular exchange. The number of dropped packets is calculated as + the difference between the number of sent packets and number of + response packets received from the server. In some cases, the + server will have sent a reponse but perfdhcp execution ended before + the reponse arrived. In such case this packet will be counted + as dropped.

  • orphans - number of packets that have been received from the server and did not match any packet sent by perfdhcp. This may occur if received packet has been sent - to some other host or if exchange time out has occured and - has been been garbage collected.

  • min delay - minimum delay that occured between - sending the packet to the server and receiving reponse from + to some other host or if then exchange timed out and + the sent packet was removed from perfdhcp's list of packets + awaiting a response.

  • min delay - minimum delay that occured between + sending the packet to the server and receiving a reponse from it.

  • avg delay - average delay between sending the - packet of the specific type the server and receiving response - from it.

  • max delay - maximum delat that occured between - sedning the packet to the server and receiveing response from - it.

  • std deviation - standard deviation of delay + packet of the specific type the server and receiving a response + from it.

  • max delay - maximum delay that occured between + sending the packet to the server and receiving a response from + it.

  • std deviation - standard deviation of the delay between sending the packet of a specific type to the server and - receiving response from it.

  • collected packets - number of garbage collected - sent packets. Packets may get garbage collected when waiting time - for server response exceeds value set with + receiving response from it.

  • collected packets - number of sent packets that + were garbage collected. Packets may get garbage collected when + the waiting time for server a response exceeds value set with the + '-d' (drop time) switch. -d<drop-time>.

  • - perfdhcp allows to run the test using specified interface: + Note: should multiple interfaces on the system running perfdhcp be + connected to the same subnet, the interface to be used for the test + can be specified using either the interface name:

    #./perfdhcp -l eth3

    - or local address assigned to it: + or a local address assigned to it:

    #./perfdhcp -l 172.16.1.2

    -

    Example: rate control

    - In the examples above perfdhcp initiates new exchanges with best - effort rate. In this case many packets is expected to be dropped by the - server due to performance limitations. Many times it is desired to set - the expected (reasonable) rate and verify if generated traffic is - handled without packet dropes by DHCP server. The following command will - make perfdhcp to initiate 300 4-way exchanges per second and test will - last for 60 seconds: +

    4.5.2. Example: rate control

    + In the examples above perfdhcp initiates new exchanges with a best + effort rate. With this setting, many packets are expected to be dropped + by the server due to performance limitations. In many cases though, it is + desired to verify that the server can handle an expected (reasonable) rate + without dropping any packets. The following command is an example of such + a test: it causes perfdhcp to initiate 300 four-way exchanges + per second, and runs the test for 60 seconds:

    #./perfdhcp -l eth3 -p 60 -r 300
     ***Rate statistics***
     Rate: 256.683 exchanges/second, expected rate: 300 exchanges/second
    @@ -515,12 +516,13 @@ max delay: 576.749 ms
     std deviation: 58.189 ms
     collected packets: 0
               

    - Note that in this example the packet drops have been significantly - reduced thanks to setting reasonable rate. The non-zero number of - packet drops and achived rate (256/s) below expected rate (300/s) - indicate that server's measured performance is lower than 300 leases - per second. Further rate decrease should eliminate most of the packet - drops and bring achived rate close to expected rate: + Note that here, the packet drops for the DISCOVER-OFFER + exchange have been significantly reduced (when compared with the + output from the previous example) thanks to the setting of a + reasonable rate. The non-zero number of packet drops and achieved + rate (256/s) indicate that server's measured performance is lower than 300 leases + per second. A further rate decrease should eliminate most of the packet + drops and bring the achieved rate close to expected rate:

    #./perfdhcp -l eth3 -p 60 -r 100 -R 30
     ***Rate statistics***
     Rate: 99.8164 exchanges/second, expected rate: 100 exchanges/second
    @@ -549,21 +551,24 @@ max delay: 189.658 ms
     std deviation: 5.876 ms
     collected packets: 0
               

    + There are now no packet drops, confirming that the server is able to + handle a load of 100 leases/second. Note that the last parameter (-R 30) configures perfdhcp to simulate - traffic from distinct 30 clients. -

    Example: templates

    - By default the DHCP messages are formed in-flight with default options. - If desired, there is a way to define custom packet format with template - files. Content in template files is encoded in hexadecimal format. The + traffic from 30 distinct clients. +

    4.5.3. Example: templates

    + By default the DHCP messages are formed with default options. With + template files, it is possible to define a custom packet format. +

    + The content in template files is encoded in hexadecimal format. perfdhcp forms the packet by replacing parts of the binary stream read - from the file with variable data such as elapsed time, HW address, DUID + from the file with variable data such as elapsed time, hardware address, DUID etc. The offsets where such variable data is placed is specific to the template file and have to be specified from the command line. Refer to - the section called “Command line options” to find out how to + Section 4.3, “Command line options” to find out how to specify offsets for particular options and fields. With the following command line the DHCPv6 SOLICIT and REQUEST packets will be formed from - solicit-example.hex and request6-example.hex packets: -

    #./perfdhcp -6 -l eth3 -r 100 -R 20 -T templates/solicit-example.hex -T templates/request6-example.hex -O 21 -E 84 -S 22 -I 40 servers
    +          solicit.hex and request6.hex packets:
    +          

    #./perfdhcp -6 -l eth3 -r 100 -R 20 -T solicit.hex -T request6.hex -O 21 -E 84 -S 22 -I 40 servers
     ***Rate statistics***
     Rate: 99.5398 exchanges/second, expected rate: 100 exchanges/second
     
    @@ -591,10 +596,10 @@ max delay: 6.490 ms
     std deviation: 0.518 ms
     collected packets: 0
               

    - where: + where the switches have the following meaning:

    • two occurences of -O 21 - DUID's last octet - positions in SOLICIT and REQUEST respectively.

    • -E 84 - elapsed time option position in - REQUEST template

    • -S 22 - server id position in REQUEST - template

    • -I 40 - IA_NA option position in REQUEST + positions in SOLICIT and REQUEST respectively.

    • -E 84 - elapsed time option position in the + REQUEST template

    • -S 22 - server id position in the REQUEST + template

    • -I 40 - IA_NA option position in the REQUEST template

    -

    \ No newline at end of file +

    diff --git a/tests/tools/dhcp-ubench/dhcp-perf-guide.xml b/tests/tools/dhcp-ubench/dhcp-perf-guide.xml index f9b9744bf7..2b7e00318f 100644 --- a/tests/tools/dhcp-ubench/dhcp-perf-guide.xml +++ b/tests/tools/dhcp-ubench/dhcp-perf-guide.xml @@ -592,80 +592,77 @@ SQLite version: 3.7.9sourceid version is 2011-11-01 00:52:41 c7c6050ef060877ebe7
    Purpose - There is a growing need to evaluate performance of DHCP servers in - different traffic conditions to understand their bottle necks. - This helps to elimante bugs in existing DHCP software as well - as make informed decisions regarding new DHCP software designs - to significantly improve its performance. The perfdhcp tool has - been created to fill the gap in performance measurement capabilities - mostly. However, the number of implemented features and parameters - exposed to the user make this tool useful for functional testing as - well. + Evaluation of the performance of a DHCP server requires that it + be tested under varying traffic loads. perfdhcp is a testing + tool with the capability to create traffic loads + and generate statistics from the results. Additional features, + such as the ability to send customised DHCP packets, allow it to + be used in a wide range of functional testing.
    Key features - The perfdhcp exposes the number of command line parameters to - control DHCP message exchanges. Currently they fall back to + perfdhcp has a number of command line switches to + control DHCP message exchanges. Currently they fall into the following categories: - + Rate control - control how many DHCP exchanges - are initiated within a period of time. Tool can also simulate - best effort conditions attempting to initiate as many DHCP - packet exchanges within a unit of time as possible. + are initiated within a period of time. The tool can also simulate + best effort conditions by attempting to initiate as many DHCP + packet exchanges as possible within a unit of time. - Test exit specifiers - control the conditions when test - completes including number of initiated exchanges, test period or - maximum number of dropped packets. + Test exit specifiers - control the conditions for test + completion, including the number of initiated exchanges, + the test period orthe maximum number of dropped packets. Packet templates - specify files containing packet templates that - are used by perfdhcp to create custom DHCP messages instead of - default. Tool also allows to specify number of values indicating - offsets of variable values within a packet that are modified in - flight by the tool. + are used by perfdhcp to create custom DHCP messages. The tool + allows the specification of a number of values indicating + offsets of values within a packet that are set by the tool. - Reporting - for each test produce the set of performance data - including achieved packet exchange rate (server performance). - There is also a number of diagnostic selectors available that - enable periodic (intermediate) reporting, packet timestamps - printing and detailed information about perfdhcp internal + Reporting - for each test produce a set of performance data + including the achieved packet exchange rate (server performance). + There are a number of diagnostic selectors available that + enable periodic (intermediate) reporting, printing of packet timestamps, + and the listing of detailed information about internal perfdhcp states (for debugging). - Different mode of operations - specify DHCP version used - (v4 or v6), 2-way or 4-way exchanges, use Rapid Commit option - for DHCPv6. + Different mode of operations - specify the DHCP protocol used + (v4 or v6), two-way or four-way exchanges, use of the + Rapid Commit option for DHCPv6. - IP layer options - specify local/remote address, local interface + IP layer options - specify the local/remote address, local interface and local port to be used for communication with DHCP server. - +
    Command line options - The following command line options may be used with perfdhcp tool. - This summary also presents its possible exit codes as well as - error counters printed along when test is complete: + The following "help" output from the tool describes the + command line switches. This summary also lists the tool's + possible exit codes as well as describing the + error counters printed when the test is complete: $ ./perfdhcp -h ] [-t] [-R] [-b] [-n] [-p] [-d] [-D] @@ -794,46 +791,51 @@ The exit status is:
    Starting perfdhcp - In order to run performance test at least two separate systems - have to be installed: client and server. The first one has to have - perfdhcp tool installed, the latter has to have DHCP server - running (v4 or v6). If only single system is available the client + In order to run a performance test, at least two separate systems + have to be installed: client and server. The first one must have + perfdhcp installed, and the latter must be running the DHCP server + (either v4 or v6). If only single system is available the client and server can be run on virtual machines (running on the same - phisical system) but in this case performance data may be heavily - impacted by the performance of VMs. - - - The DHCP operates on low port numbers (67 for DHCPv4 relays and - 547 for DHCPv6). Running perfdhcp with non-root priviliges will - usually result in the error message similar to this: - $./perfdhcp -4 -l eth3 -r 100 all -Error running perfdhcp: Failed to bind socket 3 to 172.16.1.2/port=67 - - perfdhcp has the '-L' command line option that - imposes use of custom local port. Thus the following command - line will work: - $./perfdhcp -4 -l eth3 -r 100 -L 10067 all - but in the standard configuration no responses will be received - from the ISC DHCP server because server responds to default relay - port 67. - Alternative way to overcome this issue is to run perfdhcp as root. + physical system) but in this case performance data may be heavily + impacted by the overhead involved in running such the virtual + machines. Currently, perfdhcp is seen from the server perspective as relay agent. - This simplifies its implementation (specifically there is no need to - receive traffic sent to braodcast addresses). This imposes that IPv4 + This simplifies its implementation: specifically there is no need to + receive traffic sent to braodcast addresses. However, it does impose + a requirement that the IPv4 address has to be set manually on the interface that will be used to - communicate with the server. For example, if DHCPv4 server is listening - on the interface connected to 172.16.1.0 subnet, interface on client - machine has to have network address assigned from the same subnet - on one of its interfaces connected to this subnet: + communicate with the server. For example, if the DHCPv4 server is listening + on the interface connected to the 172.16.1.0 subnet, the interface on client + machine has to have network address assigned from the same subnet, e.g. #ifconfig eth3 172.16.1.2. netmask 255.255.255.0 up + + As DHCP uses low port numbers (67 for DHCPv4 relays and + 547 for DHCPv6), running perfdhcp with non-root privileges will + usually result in the error message similar to this: + + $./perfdhcp -4 -l eth3 -r 100 all +Error running perfdhcp: Failed to bind socket 3 to 172.16.1.2/port=67 + + + The '-L' command line switch allows the use of a custom local port. + However, although the following command line will work: + + $./perfdhcp -4 -l eth3 -r 100 -L 10067 all + + in the standard configuration no responses will be received + from the DHCP server because the server responds to default relay + port 67. A way to overcome this issue is to run + perfdhcp as root. + +
    perfdhcp command line examples - In this section the perfdhcp command line examples + In this section, a number of perfdhcp command line examples are presented as a quick start guide for new users. For the detailed list of command line options refer to . @@ -841,8 +843,8 @@ Error running perfdhcp: Failed to bind socket 3 to 172.16.1.2/port=67
    Example: basic usage - If server is listening on interface with IPv4 address 172.16.1.1 - the simpliest perfdhcp command line will look like this: + If server is listening on interface with IPv4 address 172.16.1.1, + the simplest perfdhcp command line will look like: #./perfdhcp 172.16.1.1 ***Rate statistics*** Rate: 206.345 @@ -871,15 +873,13 @@ max delay: 258.634 ms std deviation: 56.936 ms collected packets: 0 - In this case perfdhcp will use remote address 172.16.1.1 as a - destination address and will use suitable local interface for - communication. Since, no rate control parameters have been specified - it will be initiating DHCP exchanges with the maximum possible - rate (it will try to initiate maximum number of exchanges per - second and count number of completed exchanged). Due to server's - performance constraints, many DHCP packets sent to server are likely - to be dropped. The performance test will be running until it is - not interrupted by the user (with ^C). + Here, perfdhcp uses remote address 172.16.1.1 as a + destination address and will use a suitable local interface for + communication. Since, no rate control parameters have been specified, + it will initiate DHCP exchanges at the maximum possible rate. Due to the server's + performance limitation, it is likely that many of the packets will be dropped. + The performance test will continue running until it is + interrupted by the user (with ^C). The default performance statistics reported by perfdhcp have the @@ -893,52 +893,56 @@ collected packets: 0 received packets - total number of DHCP packets of specific type received from the server. drops - number of dropped packets for the - particular exchange. Number of dropped packets is calculated as - a difference between number of sent packets and number of - response packets received from the server. It is likely that - server sent the reponse but perfdhcp execution had ended before - reponse arrived. In such case this packet will be assumed - dropped. + particular exchange. The number of dropped packets is calculated as + the difference between the number of sent packets and number of + response packets received from the server. In some cases, the + server will have sent a reponse but perfdhcp execution ended before + the reponse arrived. In such case this packet will be counted + as dropped. orphans - number of packets that have been received from the server and did not match any packet sent by perfdhcp. This may occur if received packet has been sent - to some other host or if exchange time out has occured and - has been been garbage collected. + to some other host or if then exchange timed out and + the sent packet was removed from perfdhcp's list of packets + awaiting a response. min delay - minimum delay that occured between - sending the packet to the server and receiving reponse from + sending the packet to the server and receiving a reponse from it. avg delay - average delay between sending the - packet of the specific type the server and receiving response + packet of the specific type the server and receiving a response from it. - max delay - maximum delat that occured between - sedning the packet to the server and receiveing response from + max delay - maximum delay that occured between + sending the packet to the server and receiving a response from it. - std deviation - standard deviation of delay + std deviation - standard deviation of the delay between sending the packet of a specific type to the server and receiving response from it. - collected packets - number of garbage collected - sent packets. Packets may get garbage collected when waiting time - for server response exceeds value set with + collected packets - number of sent packets that + were garbage collected. Packets may get garbage collected when + the waiting time for server a response exceeds value set with the + '-d' (drop time) switch. ]]>. - perfdhcp allows to run the test using specified interface: + Note: should multiple interfaces on the system running perfdhcp be + connected to the same subnet, the interface to be used for the test + can be specified using either the interface name: #./perfdhcp -l eth3 - or local address assigned to it: + or a local address assigned to it: #./perfdhcp -l 172.16.1.2
    Example: rate control - In the examples above perfdhcp initiates new exchanges with best - effort rate. In this case many packets is expected to be dropped by the - server due to performance limitations. Many times it is desired to set - the expected (reasonable) rate and verify if generated traffic is - handled without packet dropes by DHCP server. The following command will - make perfdhcp to initiate 300 4-way exchanges per second and test will - last for 60 seconds: + In the examples above perfdhcp initiates new exchanges with a best + effort rate. With this setting, many packets are expected to be dropped + by the server due to performance limitations. In many cases though, it is + desired to verify that the server can handle an expected (reasonable) rate + without dropping any packets. The following command is an example of such + a test: it causes perfdhcp to initiate 300 four-way exchanges + per second, and runs the test for 60 seconds: #./perfdhcp -l eth3 -p 60 -r 300 ***Rate statistics*** Rate: 256.683 exchanges/second, expected rate: 300 exchanges/second @@ -967,12 +971,13 @@ max delay: 576.749 ms std deviation: 58.189 ms collected packets: 0 - Note that in this example the packet drops have been significantly - reduced thanks to setting reasonable rate. The non-zero number of - packet drops and achived rate (256/s) below expected rate (300/s) - indicate that server's measured performance is lower than 300 leases - per second. Further rate decrease should eliminate most of the packet - drops and bring achived rate close to expected rate: + Note that here, the packet drops for the DISCOVER-OFFER + exchange have been significantly reduced (when compared with the + output from the previous example) thanks to the setting of a + reasonable rate. The non-zero number of packet drops and achieved + rate (256/s) indicate that server's measured performance is lower than 300 leases + per second. A further rate decrease should eliminate most of the packet + drops and bring the achieved rate close to expected rate: #./perfdhcp -l eth3 -p 60 -r 100 -R 30 ***Rate statistics*** Rate: 99.8164 exchanges/second, expected rate: 100 exchanges/second @@ -1001,25 +1006,29 @@ max delay: 189.658 ms std deviation: 5.876 ms collected packets: 0 + There are now no packet drops, confirming that the server is able to + handle a load of 100 leases/second. Note that the last parameter (-R 30) configures perfdhcp to simulate - traffic from distinct 30 clients. + traffic from 30 distinct clients.
    Example: templates - By default the DHCP messages are formed in-flight with default options. - If desired, there is a way to define custom packet format with template - files. Content in template files is encoded in hexadecimal format. The + By default the DHCP messages are formed with default options. With + template files, it is possible to define a custom packet format. + + + The content in template files is encoded in hexadecimal format. perfdhcp forms the packet by replacing parts of the binary stream read - from the file with variable data such as elapsed time, HW address, DUID + from the file with variable data such as elapsed time, hardware address, DUID etc. The offsets where such variable data is placed is specific to the template file and have to be specified from the command line. Refer to to find out how to specify offsets for particular options and fields. With the following command line the DHCPv6 SOLICIT and REQUEST packets will be formed from - solicit-example.hex and request6-example.hex packets: - #./perfdhcp -6 -l eth3 -r 100 -R 20 -T templates/solicit-example.hex -T templates/request6-example.hex -O 21 -E 84 -S 22 -I 40 servers + solicit.hex and request6.hex packets: + #./perfdhcp -6 -l eth3 -r 100 -R 20 -T solicit.hex -T request6.hex -O 21 -E 84 -S 22 -I 40 servers ***Rate statistics*** Rate: 99.5398 exchanges/second, expected rate: 100 exchanges/second @@ -1047,15 +1056,15 @@ max delay: 6.490 ms std deviation: 0.518 ms collected packets: 0 - where: + where the switches have the following meaning: two occurences of -O 21 - DUID's last octet positions in SOLICIT and REQUEST respectively. - -E 84 - elapsed time option position in + -E 84 - elapsed time option position in the REQUEST template - -S 22 - server id position in REQUEST + -S 22 - server id position in the REQUEST template - -I 40 - IA_NA option position in REQUEST + -I 40 - IA_NA option position in the REQUEST template From c7a025395cf01c95cee3430fdd94256ab3350244 Mon Sep 17 00:00:00 2001 From: Stephen Morris Date: Wed, 12 Sep 2012 15:32:58 +0100 Subject: [PATCH 149/199] [1545] Corrected copyright year in new/modified files --- src/bin/dhcp4/dhcp4_log.cc | 2 +- src/bin/dhcp4/dhcp4_log.h | 2 +- src/bin/dhcp4/dhcp4_messages.mes | 2 +- src/bin/dhcp4/tests/dhcp4_srv_unittest.cc | 2 +- src/bin/dhcp6/dhcp6_log.cc | 2 +- src/bin/dhcp6/dhcp6_log.h | 2 +- src/bin/dhcp6/dhcp6_messages.mes | 2 +- src/bin/dhcp6/tests/dhcp6_srv_unittest.cc | 4 ++-- 8 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/bin/dhcp4/dhcp4_log.cc b/src/bin/dhcp4/dhcp4_log.cc index 51c18df455..678223b134 100644 --- a/src/bin/dhcp4/dhcp4_log.cc +++ b/src/bin/dhcp4/dhcp4_log.cc @@ -1,4 +1,4 @@ -// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC") +// Copyright (C) 2012 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 diff --git a/src/bin/dhcp4/dhcp4_log.h b/src/bin/dhcp4/dhcp4_log.h index 9d05818f3f..3717b62d92 100644 --- a/src/bin/dhcp4/dhcp4_log.h +++ b/src/bin/dhcp4/dhcp4_log.h @@ -1,4 +1,4 @@ -// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC") +// Copyright (C) 2012 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 diff --git a/src/bin/dhcp4/dhcp4_messages.mes b/src/bin/dhcp4/dhcp4_messages.mes index b149b25322..87b3d1a6f0 100644 --- a/src/bin/dhcp4/dhcp4_messages.mes +++ b/src/bin/dhcp4/dhcp4_messages.mes @@ -1,4 +1,4 @@ -# Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC") +# Copyright (C) 2012 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 diff --git a/src/bin/dhcp4/tests/dhcp4_srv_unittest.cc b/src/bin/dhcp4/tests/dhcp4_srv_unittest.cc index 08be95c47f..fca06ad988 100644 --- a/src/bin/dhcp4/tests/dhcp4_srv_unittest.cc +++ b/src/bin/dhcp4/tests/dhcp4_srv_unittest.cc @@ -1,4 +1,4 @@ -// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC") +// Copyright (C) 2011-2012 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 diff --git a/src/bin/dhcp6/dhcp6_log.cc b/src/bin/dhcp6/dhcp6_log.cc index 9816f47b03..d89383492c 100644 --- a/src/bin/dhcp6/dhcp6_log.cc +++ b/src/bin/dhcp6/dhcp6_log.cc @@ -1,4 +1,4 @@ -// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC") +// Copyright (C) 2012 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 diff --git a/src/bin/dhcp6/dhcp6_log.h b/src/bin/dhcp6/dhcp6_log.h index 8db11dbad0..6d7f4e33b0 100644 --- a/src/bin/dhcp6/dhcp6_log.h +++ b/src/bin/dhcp6/dhcp6_log.h @@ -1,4 +1,4 @@ -// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC") +// Copyright (C) 2012 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 diff --git a/src/bin/dhcp6/dhcp6_messages.mes b/src/bin/dhcp6/dhcp6_messages.mes index f2e6fda2c9..f43bf09a6f 100644 --- a/src/bin/dhcp6/dhcp6_messages.mes +++ b/src/bin/dhcp6/dhcp6_messages.mes @@ -1,4 +1,4 @@ -# Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC") +# Copyright (C) 2012 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 diff --git a/src/bin/dhcp6/tests/dhcp6_srv_unittest.cc b/src/bin/dhcp6/tests/dhcp6_srv_unittest.cc index 0e494e972b..f1ce8f704b 100644 --- a/src/bin/dhcp6/tests/dhcp6_srv_unittest.cc +++ b/src/bin/dhcp6/tests/dhcp6_srv_unittest.cc @@ -1,4 +1,4 @@ -// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC") +// Copyright (C) 2011-2012 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 @@ -268,4 +268,4 @@ TEST_F(Dhcpv6SrvTest, serverReceivedPacketName) { } } -} // oF ANONYMOUS NAMESPACE +} // anonymous namespace From 23ba7abb4e2fe826bf536c822fb7eef20ebcd689 Mon Sep 17 00:00:00 2001 From: Stephen Morris Date: Wed, 12 Sep 2012 15:58:00 +0100 Subject: [PATCH 150/199] [1545] Minor changes as suggested by review --- src/bin/dhcp4/dhcp4_messages.mes | 6 +++--- src/bin/dhcp4/dhcp4_srv.h | 10 +++++----- src/bin/dhcp6/dhcp6_messages.mes | 4 ++-- src/bin/dhcp6/dhcp6_srv.h | 6 +++--- src/bin/dhcp6/tests/dhcp6_srv_unittest.cc | 2 +- 5 files changed, 14 insertions(+), 14 deletions(-) diff --git a/src/bin/dhcp4/dhcp4_messages.mes b/src/bin/dhcp4/dhcp4_messages.mes index 87b3d1a6f0..98e402da84 100644 --- a/src/bin/dhcp4/dhcp4_messages.mes +++ b/src/bin/dhcp4/dhcp4_messages.mes @@ -40,7 +40,7 @@ server is about to open sockets on the specified port. % DHCP4_PACKET_PARSE_FAIL failed to parse incoming packet: %1 The IPv4 DHCP server has received a packet that it is unable to -interpret. The reason why the packet is invalid is include in the message. +interpret. The reason why the packet is invalid is included in the message. % DHCP4_PACKET_RECEIVED %1 (type %2) packet received on interface %3 A debug message noting that the server has received the specified type of @@ -50,8 +50,8 @@ may well be a valid DHCP packet, just a type not expected by the server % DHCP4_PACK_FAIL failed to assemble response correctly This error is output if the server failed to assemble the data to be -returned to the client into a valid packet. Additional messages will -detail the reason. +returned to the client into a valid packet. The cause is most likely +to be a programming error: please raise a bug report. % DHCP4_QUERY_DATA received packet type %1, data is <%2> A debug message listing the data received from the client. diff --git a/src/bin/dhcp4/dhcp4_srv.h b/src/bin/dhcp4/dhcp4_srv.h index ce09f5313f..f677259947 100644 --- a/src/bin/dhcp4/dhcp4_srv.h +++ b/src/bin/dhcp4/dhcp4_srv.h @@ -44,7 +44,7 @@ class Dhcpv4Srv : public boost::noncopyable { public: /// @brief Default constructor. /// - /// Instantiates necessary services, required to run DHCPv6 server. + /// Instantiates necessary services, required to run DHCPv4 server. /// In particular, creates IfaceMgr that will be responsible for /// network interaction. Will instantiate lease manager, and load /// old or create new DUID. It is possible to specify alternate @@ -54,7 +54,7 @@ class Dhcpv4Srv : public boost::noncopyable { /// @param port specifies port number to listen on Dhcpv4Srv(uint16_t port = DHCP4_SERVER_PORT); - /// @brief Destructor. Used during DHCPv6 service shutdown. + /// @brief Destructor. Used during DHCPv4 service shutdown. ~Dhcpv4Srv(); /// @brief Main server processing loop. @@ -75,7 +75,7 @@ class Dhcpv4Srv : public boost::noncopyable { /// Returns the name of valid packet received by the server (e.g. DISCOVER). /// If the packet is unknown - or if it is a valid DHCP packet but not one /// expected to be received by the server (such as an OFFER), the string - /// "UNKNOWN" is returned. This methos is used in debug messages. + /// "UNKNOWN" is returned. This method is used in debug messages. /// /// As the operation of the method does not depend on any server state, it /// is declared static. @@ -106,11 +106,11 @@ protected: /// is valid, not expired, not reserved, not used by other client and /// that requesting client is allowed to use it. /// - /// Returns ACK message, NACK message, or NULL + /// Returns ACK message, NAK message, or NULL /// /// @param request a message received from client /// - /// @return ACK or NACK message + /// @return ACK or NAK message Pkt4Ptr processRequest(Pkt4Ptr& request); /// @brief Stub function that will handle incoming RELEASE messages. diff --git a/src/bin/dhcp6/dhcp6_messages.mes b/src/bin/dhcp6/dhcp6_messages.mes index f43bf09a6f..a3615319a7 100644 --- a/src/bin/dhcp6/dhcp6_messages.mes +++ b/src/bin/dhcp6/dhcp6_messages.mes @@ -53,8 +53,8 @@ a received OFFER packet as UNKNOWN). % DHCP6_PACK_FAIL failed to assemble response correctly This error is output if the server failed to assemble the data to be -returned to the client into a valid packet. Additional messages will -detail the reason. +returned to the client into a valid packet. The reason is most likely +to be to a programming error: please raise a bug report. % DHCP6_QUERY_DATA received packet length %1, data length %2, data is <%3> A debug message listing the data received from the client or relay. diff --git a/src/bin/dhcp6/dhcp6_srv.h b/src/bin/dhcp6/dhcp6_srv.h index 3ef57fdb19..1cb323657a 100644 --- a/src/bin/dhcp6/dhcp6_srv.h +++ b/src/bin/dhcp6/dhcp6_srv.h @@ -72,10 +72,10 @@ public: /// @brief Return textual type of packet received by server /// - /// Returns the name of valid packet received by the server (e.g. DISCOVER). + /// Returns the name of valid packet received by the server (e.g. SOLICIT). /// If the packet is unknown - or if it is a valid DHCP packet but not one - /// expected to be received by the server (such as an OFFER), the string - /// "UNKNOWN" is returned. This methos is used in debug messages. + /// expected to be received by the server (such as an ADVERTISE), the string + /// "UNKNOWN" is returned. This method is used in debug messages. /// /// As the operation of the method does not depend on any server state, it /// is declared static. diff --git a/src/bin/dhcp6/tests/dhcp6_srv_unittest.cc b/src/bin/dhcp6/tests/dhcp6_srv_unittest.cc index f1ce8f704b..20287066da 100644 --- a/src/bin/dhcp6/tests/dhcp6_srv_unittest.cc +++ b/src/bin/dhcp6/tests/dhcp6_srv_unittest.cc @@ -268,4 +268,4 @@ TEST_F(Dhcpv6SrvTest, serverReceivedPacketName) { } } -} // anonymous namespace +} // end of anonymous namespace From faf0803218384ff5b61ade0bc13e842dcd384ffd Mon Sep 17 00:00:00 2001 From: Marcin Siodelski Date: Wed, 12 Sep 2012 17:43:20 +0200 Subject: [PATCH 151/199] [2231] Implemented suggestions from code review. --- src/lib/dhcp/iface_mgr.cc | 8 ++-- src/lib/dhcp/tests/iface_mgr_unittest.cc | 52 +++++++++++++----------- 2 files changed, 34 insertions(+), 26 deletions(-) diff --git a/src/lib/dhcp/iface_mgr.cc b/src/lib/dhcp/iface_mgr.cc index 0c99647e27..9570cf5e3a 100644 --- a/src/lib/dhcp/iface_mgr.cc +++ b/src/lib/dhcp/iface_mgr.cc @@ -834,7 +834,8 @@ IfaceMgr::receive4(uint32_t timeout_sec, uint32_t timeout_usec) { select_timeout.tv_usec = timeout_usec; cout << "Trying to receive data on sockets: " << names.str() - << ". Timeout is " << timeout_sec << " seconds." << endl; + << ". Timeout is " << timeout_sec << "." << setw(6) << setfill('0') + << timeout_usec << " seconds." << endl; int result = select(maxfd + 1, &sockets, NULL, NULL, &select_timeout); cout << "select returned " << result << endl; @@ -1001,8 +1002,9 @@ Pkt6Ptr IfaceMgr::receive6(uint32_t timeout_sec, uint32_t timeout_usec) { names << session_socket_ << "(session)"; } - cout << "Trying to receive data on sockets:" << names.str() - << ".Timeout is " << timeout_sec << " seconds." << endl; + cout << "Trying to receive data on sockets: " << names.str() + << ". Timeout is " << timeout_sec << "." << setw(6) << setfill('0') + << timeout_usec << " seconds." << endl; struct timeval select_timeout; select_timeout.tv_sec = timeout_sec; diff --git a/src/lib/dhcp/tests/iface_mgr_unittest.cc b/src/lib/dhcp/tests/iface_mgr_unittest.cc index 3076658a6f..13d471a8e3 100644 --- a/src/lib/dhcp/tests/iface_mgr_unittest.cc +++ b/src/lib/dhcp/tests/iface_mgr_unittest.cc @@ -238,10 +238,12 @@ TEST_F(IfaceMgrTest, receiveTimeout6) { timeval stop_time; // Remember when we call receive6(). + memset(&start_time, 0, sizeof(start_time)); + memset(&stop_time, 0, sizeof(start_time)); gettimeofday(&start_time, NULL); - // Call receive with timeout of 1s + 1000us. + // Call receive with timeout of 1s + 400000us = 1.4s. Pkt6Ptr pkt; - ASSERT_NO_THROW(pkt = ifacemgr->receive6(1, 1000)); + ASSERT_NO_THROW(pkt = ifacemgr->receive6(1, 400000)); // Remember when call to receive6() ended. gettimeofday(&stop_time, NULL); // We did not send a packet to lo interface so we expect that @@ -250,25 +252,26 @@ TEST_F(IfaceMgrTest, receiveTimeout6) { // Calculate duration of call to receive6(). stop_time.tv_sec -= start_time.tv_sec; stop_time.tv_usec -= start_time.tv_usec; - // Duration should be equal or greater than timeout specified + // Duration should be equal or greater or equal timeout specified // for the receive6() call. - EXPECT_EQ(stop_time.tv_sec, 1); - EXPECT_GT(stop_time.tv_usec, 1000); + EXPECT_EQ(1, stop_time.tv_sec); + EXPECT_GE(stop_time.tv_usec, 400000); // Test timeout shorter than 1s. + memset(&start_time, 0, sizeof(start_time)); + memset(&stop_time, 0, sizeof(start_time)); gettimeofday(&start_time, NULL); - ASSERT_NO_THROW(pkt = ifacemgr->receive6(0, 500)); + ASSERT_NO_THROW(pkt = ifacemgr->receive6(0, 500000)); gettimeofday(&stop_time, NULL); ASSERT_FALSE(pkt); stop_time.tv_sec -= start_time.tv_sec; stop_time.tv_usec -= start_time.tv_usec; - // The timeout has been set to 500us. Even though the way we measure - // duration of receive6() may result in durations slightly longer than - // timeout it is safe to assume that measured value will not exceed 1s - // when timeout is only 500us. If it exceeds, this is an error and - // should be investigated. + // Even though the way we measure duration of receive6() may result in + // durations slightly longer than timeout it is safe to assume that + // measured value will not exceed 1s when timeout is 0.5s. + // If it exceeds, this is an error and should be investigated. EXPECT_EQ(0, stop_time.tv_sec); - EXPECT_GT(stop_time.tv_usec, 500); + EXPECT_GE(stop_time.tv_usec, 500000); // Test with invalid fractional timeout values. EXPECT_THROW(ifacemgr->receive6(0, 1000000), isc::BadValue); @@ -296,10 +299,12 @@ TEST_F(IfaceMgrTest, receiveTimeout4) { timeval stop_time; // Remember when we call receive4(). + memset(&start_time, 0, sizeof(start_time)); + memset(&stop_time, 0, sizeof(start_time)); gettimeofday(&start_time, NULL); - // Call receive with timeout of 2s + 150us. + // Call receive with timeout of 2s + 300000us = 2.3s. Pkt4Ptr pkt; - ASSERT_NO_THROW(pkt = ifacemgr->receive4(2, 150)); + ASSERT_NO_THROW(pkt = ifacemgr->receive4(2, 300000)); // Remember when call to receive4() ended. gettimeofday(&stop_time, NULL); // We did not send a packet to lo interface so we expect that @@ -310,23 +315,24 @@ TEST_F(IfaceMgrTest, receiveTimeout4) { stop_time.tv_usec -= start_time.tv_usec; // Duration should be equal or greater than timeout specified // for the receive4() call. - EXPECT_EQ(stop_time.tv_sec, 2); - EXPECT_GT(stop_time.tv_usec, 150); + EXPECT_EQ(2, stop_time.tv_sec); + EXPECT_GE(stop_time.tv_usec, 300000); // Test timeout shorter than 1s. + memset(&start_time, 0, sizeof(start_time)); + memset(&stop_time, 0, sizeof(start_time)); gettimeofday(&start_time, NULL); - ASSERT_NO_THROW(pkt = ifacemgr->receive4(0, 350)); + ASSERT_NO_THROW(pkt = ifacemgr->receive4(0, 400000)); gettimeofday(&stop_time, NULL); ASSERT_FALSE(pkt); stop_time.tv_sec -= start_time.tv_sec; stop_time.tv_usec -= start_time.tv_usec; - // The timeout has been set to 350us. Even though the way we measure - // duration of receive4() may result in durations slightly longer than - // timeout it is safe to assume that measured value will not exceed 1s - // when timeout is only 350us. If it exceeds, this is an error and - // should be investigated. + // Even though the way we measure duration of receive4() may result + // in durations slightly longer than timeout it is safe to assume + // that measured value will not exceed 1s when timeout is only 0.4s. + // If it exceeds, this is an error and should be investigated. EXPECT_EQ(0, stop_time.tv_sec); - EXPECT_GT(stop_time.tv_usec, 350); + EXPECT_GE(stop_time.tv_usec, 400000); // Test with invalid fractional timeout values. EXPECT_THROW(ifacemgr->receive6(0, 1000000), isc::BadValue); From 53e77f34ca655fc29320bba05a12a98355c365fb Mon Sep 17 00:00:00 2001 From: Marcin Siodelski Date: Thu, 13 Sep 2012 09:10:30 +0200 Subject: [PATCH 152/199] [2231] Simplify the duration measurements with boost::posix_time. --- src/lib/dhcp/tests/iface_mgr_unittest.cc | 92 +++++++++++------------- 1 file changed, 42 insertions(+), 50 deletions(-) diff --git a/src/lib/dhcp/tests/iface_mgr_unittest.cc b/src/lib/dhcp/tests/iface_mgr_unittest.cc index 13d471a8e3..adcba3152a 100644 --- a/src/lib/dhcp/tests/iface_mgr_unittest.cc +++ b/src/lib/dhcp/tests/iface_mgr_unittest.cc @@ -218,6 +218,7 @@ TEST_F(IfaceMgrTest, getIface) { } TEST_F(IfaceMgrTest, receiveTimeout6) { + using namespace boost::posix_time; std::cout << "Testing DHCPv6 packet reception timeouts." << " Test will block for a few seconds when waiting" << " for timeout to occur." << std::endl; @@ -233,45 +234,40 @@ TEST_F(IfaceMgrTest, receiveTimeout6) { ASSERT_GT(socket1, 0); // Time when call to IfaceMgr::receive6() started. - timeval start_time; + ptime start_time; // Time when call to IfaceMgr::receive6() ended. - timeval stop_time; + ptime stop_time; + // Time duration between start_time and stop_time. + time_duration duration; // Remember when we call receive6(). - memset(&start_time, 0, sizeof(start_time)); - memset(&stop_time, 0, sizeof(start_time)); - gettimeofday(&start_time, NULL); + start_time = microsec_clock::universal_time(); // Call receive with timeout of 1s + 400000us = 1.4s. Pkt6Ptr pkt; ASSERT_NO_THROW(pkt = ifacemgr->receive6(1, 400000)); // Remember when call to receive6() ended. - gettimeofday(&stop_time, NULL); + stop_time = microsec_clock::universal_time(); // We did not send a packet to lo interface so we expect that // nothing has been received and timeout has been reached. ASSERT_FALSE(pkt); // Calculate duration of call to receive6(). - stop_time.tv_sec -= start_time.tv_sec; - stop_time.tv_usec -= start_time.tv_usec; - // Duration should be equal or greater or equal timeout specified - // for the receive6() call. - EXPECT_EQ(1, stop_time.tv_sec); - EXPECT_GE(stop_time.tv_usec, 400000); + duration = stop_time - start_time; + // We stop the clock when the call completes so it does not + // precisely reflect the receive timeout. However the + // uncertainity should be low enough to expect that measured + // value is in the range <1.4; 2). + EXPECT_GE(duration.total_microseconds(), 1400000); + EXPECT_LT(duration.total_seconds(), 2); // Test timeout shorter than 1s. - memset(&start_time, 0, sizeof(start_time)); - memset(&stop_time, 0, sizeof(start_time)); - gettimeofday(&start_time, NULL); + start_time = microsec_clock::universal_time(); ASSERT_NO_THROW(pkt = ifacemgr->receive6(0, 500000)); - gettimeofday(&stop_time, NULL); + stop_time = microsec_clock::universal_time(); ASSERT_FALSE(pkt); - stop_time.tv_sec -= start_time.tv_sec; - stop_time.tv_usec -= start_time.tv_usec; - // Even though the way we measure duration of receive6() may result in - // durations slightly longer than timeout it is safe to assume that - // measured value will not exceed 1s when timeout is 0.5s. - // If it exceeds, this is an error and should be investigated. - EXPECT_EQ(0, stop_time.tv_sec); - EXPECT_GE(stop_time.tv_usec, 500000); + duration = stop_time - start_time; + // Check if measured duration is within <0.5s; 1s). + EXPECT_GE(duration.total_microseconds(), 500000); + EXPECT_LT(duration.total_seconds(), 1); // Test with invalid fractional timeout values. EXPECT_THROW(ifacemgr->receive6(0, 1000000), isc::BadValue); @@ -279,6 +275,7 @@ TEST_F(IfaceMgrTest, receiveTimeout6) { } TEST_F(IfaceMgrTest, receiveTimeout4) { + using namespace boost::posix_time; std::cout << "Testing DHCPv6 packet reception timeouts." << " Test will block for a few seconds when waiting" << " for timeout to occur." << std::endl; @@ -294,45 +291,40 @@ TEST_F(IfaceMgrTest, receiveTimeout4) { ASSERT_GT(socket1, 0); // Time when call to IfaceMgr::receive4() started. - timeval start_time; + ptime start_time; // Time when call to IfaceMgr::receive4() ended. - timeval stop_time; + ptime stop_time; + // Time duration between start_time and stop_time. + time_duration duration; - // Remember when we call receive4(). - memset(&start_time, 0, sizeof(start_time)); - memset(&stop_time, 0, sizeof(start_time)); - gettimeofday(&start_time, NULL); - // Call receive with timeout of 2s + 300000us = 2.3s. Pkt4Ptr pkt; + // Remember when we call receive4(). + start_time = microsec_clock::universal_time(); + // Call receive with timeout of 2s + 300000us = 2.3s. ASSERT_NO_THROW(pkt = ifacemgr->receive4(2, 300000)); // Remember when call to receive4() ended. - gettimeofday(&stop_time, NULL); + stop_time = microsec_clock::universal_time(); // We did not send a packet to lo interface so we expect that // nothing has been received and timeout has been reached. ASSERT_FALSE(pkt); // Calculate duration of call to receive4(). - stop_time.tv_sec -= start_time.tv_sec; - stop_time.tv_usec -= start_time.tv_usec; - // Duration should be equal or greater than timeout specified - // for the receive4() call. - EXPECT_EQ(2, stop_time.tv_sec); - EXPECT_GE(stop_time.tv_usec, 300000); + duration = stop_time - start_time; + // We stop the clock when the call completes so it does not + // precisely reflect the receive timeout. However the + // uncertainity should be low enough to expect that measured + // value is in the range <2.3s; 3s). + EXPECT_GE(duration.total_microseconds(), 2300000); + EXPECT_LT(duration.total_seconds(), 3); // Test timeout shorter than 1s. - memset(&start_time, 0, sizeof(start_time)); - memset(&stop_time, 0, sizeof(start_time)); - gettimeofday(&start_time, NULL); + start_time = microsec_clock::universal_time(); ASSERT_NO_THROW(pkt = ifacemgr->receive4(0, 400000)); - gettimeofday(&stop_time, NULL); + stop_time = microsec_clock::universal_time(); ASSERT_FALSE(pkt); - stop_time.tv_sec -= start_time.tv_sec; - stop_time.tv_usec -= start_time.tv_usec; - // Even though the way we measure duration of receive4() may result - // in durations slightly longer than timeout it is safe to assume - // that measured value will not exceed 1s when timeout is only 0.4s. - // If it exceeds, this is an error and should be investigated. - EXPECT_EQ(0, stop_time.tv_sec); - EXPECT_GE(stop_time.tv_usec, 400000); + duration = stop_time - start_time; + // Check if measured duration is within <0.4s; 1s). + EXPECT_GE(duration.total_microseconds(), 400000); + EXPECT_LT(duration.total_seconds(), 1); // Test with invalid fractional timeout values. EXPECT_THROW(ifacemgr->receive6(0, 1000000), isc::BadValue); From 250da3f615ed6d09380dc2de7e6633017530bffd Mon Sep 17 00:00:00 2001 From: Marcin Siodelski Date: Thu, 13 Sep 2012 09:37:37 +0200 Subject: [PATCH 153/199] [2231] Show default timeout_usec values in implementation. --- src/lib/dhcp/iface_mgr.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/lib/dhcp/iface_mgr.cc b/src/lib/dhcp/iface_mgr.cc index 9570cf5e3a..9a082d2d87 100644 --- a/src/lib/dhcp/iface_mgr.cc +++ b/src/lib/dhcp/iface_mgr.cc @@ -785,7 +785,7 @@ IfaceMgr::send(const Pkt4Ptr& pkt) boost::shared_ptr -IfaceMgr::receive4(uint32_t timeout_sec, uint32_t timeout_usec) { +IfaceMgr::receive4(uint32_t timeout_sec, uint32_t timeout_usec /* = 0 */) { // Sanity check for microsecond timeout. if (timeout_usec >= 1000000) { isc_throw(BadValue, "fractional timeout must be shorter than" @@ -957,7 +957,7 @@ IfaceMgr::receive4(uint32_t timeout_sec, uint32_t timeout_usec) { return (pkt); } -Pkt6Ptr IfaceMgr::receive6(uint32_t timeout_sec, uint32_t timeout_usec) { +Pkt6Ptr IfaceMgr::receive6(uint32_t timeout_sec, uint32_t timeout_usec /* = 0 */ ) { // Sanity check for microsecond timeout. if (timeout_usec >= 1000000) { isc_throw(BadValue, "fractional timeout must be shorter than" From e7c00e175ff97af761b1ac262c9a2bb7ad5588e4 Mon Sep 17 00:00:00 2001 From: Marcin Siodelski Date: Thu, 13 Sep 2012 10:24:49 +0200 Subject: [PATCH 154/199] [2230] Explain -T and -O command line options. --- tests/tools/dhcp-ubench/dhcp-perf-guide.html | 89 ++++++++++++-------- tests/tools/dhcp-ubench/dhcp-perf-guide.xml | 32 +++++-- 2 files changed, 79 insertions(+), 42 deletions(-) diff --git a/tests/tools/dhcp-ubench/dhcp-perf-guide.html b/tests/tools/dhcp-ubench/dhcp-perf-guide.html index f610476c70..34c3225513 100644 --- a/tests/tools/dhcp-ubench/dhcp-perf-guide.html +++ b/tests/tools/dhcp-ubench/dhcp-perf-guide.html @@ -1,23 +1,25 @@ -DHCP Performance Guide

    DHCP Performance Guide

    Tomasz Mrugalski

    Marcin Siodelski

    This is a companion document for BIND 10 version - 20120817.

    Abstract

    BIND 10 is a framework that features Domain Name System + + + DHCP Performance Guide

    DHCP Performance Guide

    Tomasz Mrugalski

    Marcin Siodelski

    This is a companion document for BIND 10 version + 20120817.

    Abstract

    BIND 10 is a framework that features Domain Name System (DNS) suite and Dynamic Host Configuration Protocol (DHCP) servers with development managed by Internet Systems Consortium (ISC). This document describes various aspects of DHCP performance, measurements and tuning. It covers BIND 10 DHCP (codename Kea), existing ISC DHCP4 software, perfdhcp (a DHCP performance - measurement tool) and other related topics.


    Preface

    Table of Contents

    1. Acknowledgements

    1. Acknowledgements

    ISC would like to acknowledge generous support for + measurement tool) and other related topics.


    Preface

    Table of Contents

    Acknowledgements

    Acknowledgements

    ISC would like to acknowledge generous support for BIND 10 development of DHCPv4 and DHCPv6 components provided - by Comcast.

    Chapter 1. Introduction

    + by Comcast.

    Chapter 1. Introduction

    This document is in its early stages of development. It is expected to grow significantly in a near future. It will cover topics like database backend perfomance measurements, pros an cons of various optimization techniques and tools. -

    Chapter 2. ISC DHCP 4.x

    +

    Chapter 2. ISC DHCP 4.x

    TODO: Write something about ISC DHCP4 here. -

    Chapter 3. Kea

    -

    3.1. Backend performance evaluation

    +

    Backend performance evaluation

    Kea will support several different database backends, using both popular databases (like MySQL or SQLite) and custom-developed solutions (like in-memory database). BIND 10 @@ -62,7 +64,7 @@

    The framework attempts to do the same amount of operations for every backend thus allowing fair complarison between them. -

    3.2. MySQL backend

    MySQL backend requires MySQL client development libraries. It uses +

    MySQL backend

    MySQL backend requires MySQL client development libraries. It uses mysql_config tool (that works similar to pkg-config) to discover required compilation and linking options. To install required packages on Ubuntu, use the following command: @@ -111,13 +113,13 @@ Possible command-line parameters: -c yes|no - should compiled statements be used (MySQL only)

    -

    3.2.1. MySQL tweaks

    One parameter that has huge impact on performance is a a backend engine. +

    MySQL tweaks

    One parameter that has huge impact on performance is a a backend engine. You can get a list of engines of your MySQL implementation by using

    > show engines;

    in your mysql client. Two notable engines are MyISAM and InnoDB. mysql_ubench will - use MyISAM for synchronous mode and InnoDB for asynchronous.

    3.3. SQLite-ubench

    SQLite backend requires both sqlite3 development and run-time package. Their + use MyISAM for synchronous mode and InnoDB for asynchronous.

    SQLite-ubench

    SQLite backend requires both sqlite3 development and run-time package. Their names may vary from system to system, but on Ubuntu 12.04 they are called sqlite3 libsqlite3-dev. To install them, use the following command: @@ -132,7 +134,7 @@ Possible command-line parameters:

    > ./sqlite_ubench

    or

    > ./sqlite_ubench > results-sqlite.txt

    -

    3.3.1. SQLite tweaks

    To modify default sqlite_ubench parameters, command line +

    SQLite tweaks

    To modify default sqlite_ubench parameters, command line switches can be used. Currently supported parameters are (default values specified in brackets):

    1. -f filename - name of the database file ("sqlite.db")

    2. -n num - number of iterations (100)

    3. -s yes|no - should the operations be performend in synchronous (yes) @@ -143,13 +145,13 @@ Possible command-line parameters: turned to several modes of operation. Its value can be modified in SQLite_uBenchmark::connect(). See http://www.sqlite.org/pragma.html#pragma_journal_mode for - detailed explanantion.

    3.4. memfile-ubench

    Memfile backend is custom developed prototype backend that + detailed explanantion.

    memfile-ubench

    Memfile backend is custom developed prototype backend that somewhat mimics operation of ISC DHCP4. It uses in-memory storage using standard C++ and boost mechanisms (std::map and boost::shared_ptr<>). All database changes are also written to a lease file. That file is strictly write-only. This approach takes advantage of the fact that simple append is faster - than edition with potential whole file relocation.

    3.4.1. memfile tweaks

    To modify default memfile_ubench parameters, command line + than edition with potential whole file relocation.

    memfile tweaks

    To modify default memfile_ubench parameters, command line switches can be used. Currently supported parameters are (default values specified in brackets):

    1. -f filename - name of the database file ("dhcpd.leases")

    2. -n num - number of iterations (100)

    3. -s yes|no - should the operations be performend in synchronous (yes) @@ -157,7 +159,7 @@ Possible command-line parameters:

      memfile can run in asynchronous or synchronous mode. This mode can be controlled by using sync parameter. It uses fflush() and fsync() in synchronous mode to make sure that - data is not buffered and physically stored on disk.

    3.5. Performance measurements

    This section contains sample results for backend performance measurements, + data is not buffered and physically stored on disk.

    Performance measurements

    This section contains sample results for backend performance measurements, taken using microbenchmarks. Tests were conducted on reasonably powerful machine:

     CPU: Quad-core Intel(R) Core(TM) i7-2600K CPU @ 3.40GHz (8 logical cores)
    @@ -170,13 +172,13 @@ SQLite version: 3.7.9sourceid version is 2011-11-01 00:52:41 c7c6050ef060877ebe7
           asynchronous. As those modes offer radically different
           performances, synchronous mode was conducted for 1000 (one
           thousand) repetitions and asynchronous mode was conducted for
    -      100000 (hundred thousand) repetitions.

    Table 3.1. Synchronous results

    BackendOperationsCreateSearchUpdateDeleteAverage
    MySQL100031.603978s 0.116612s27.964191s27.695209s21.844998s
    SQLite100061.421356s 0.033283s59.476638s56.034150s44.241357s
    memfile100041.711886s 0.000724s42.267578s42.169679s31.537467s

    Following parameters were measured for asynchronous mode. + 100000 (hundred thousand) repetitions.

    Table 3.1. Synchronous results

    BackendOperationsCreateSearchUpdateDeleteAverage
    MySQL100031.603978s 0.116612s27.964191s27.695209s21.844998s
    SQLite100061.421356s 0.033283s59.476638s56.034150s44.241357s
    memfile100041.711886s 0.000724s42.267578s42.169679s31.537467s

    Following parameters were measured for asynchronous mode. MySQL and SQLite were run with 100 thousand repetitions. Memfile - was run for 1 million repetitions due to much larger performance.

    Table 3.2. Asynchronous results

    BackendOperationsCreate [s]Search [s]Update [s]Delete [s]Average [s]
    MySQL10000010.584842s10.386402s10.062384s 8.890197s 9.980956s
    SQLite100000 3.710356s 3.159129s 2.865354s 2.439406s 3.043561s
    memfile1000000 (sic!) 6.084131s 0.862667s 6.018585s 5.146704s 4.528022s

    Presented performance results can be computed into operations per second metrics. + was run for 1 million repetitions due to much larger performance.

    Table 3.2. Asynchronous results

    BackendOperationsCreate [s]Search [s]Update [s]Delete [s]Average [s]
    MySQL10000010.584842s10.386402s10.062384s 8.890197s 9.980956s
    SQLite100000 3.710356s 3.159129s 2.865354s 2.439406s 3.043561s
    memfile1000000 (sic!) 6.084131s 0.862667s 6.018585s 5.146704s 4.528022s

    Presented performance results can be computed into operations per second metrics. It should be noted that due to large differences between various operations (sometime over 3 orders of magnitude), it is difficult to create a simple, readable chart with - that data.

    Table 3.3. Estimated performance

    BackendCreate [oper/s]Search [oper/s]Update [oper/s]Delete [oper/s]Average [oper/s]
    MySQL (async)9447.479627.979938.0011248.3410065.45
    SQLite (async)26951.5931654.2934899.7040993.5933624.79
    memfile (async)164362.011159195.84166152.01194299.11421002.24
    MySQL (sync)31.648575.4535.7636.112169.74
    SQLite (sync)16.2820045.3716.8117.857524.08
    memfile (sync)23.971381215.4723.6623.71345321.70

    Performance measurements

    Graphical representation of the performance results - presented in table Table 3.3, “Estimated performance”.

    3.6. Possible further optimizations

    + that data.

    Table 3.3. Estimated performance

    BackendCreate [oper/s]Search [oper/s]Update [oper/s]Delete [oper/s]Average [oper/s]
    MySQL (async)9447.479627.979938.0011248.3410065.45
    SQLite (async)26951.5931654.2934899.7040993.5933624.79
    memfile (async)164362.011159195.84166152.01194299.11421002.24
    MySQL (sync)31.648575.4535.7636.112169.74
    SQLite (sync)16.2820045.3716.8117.857524.08
    memfile (sync)23.971381215.4723.6623.71345321.70

    Performance measurements

    Graphical representation of the performance results + presented in table Table 3.3, “Estimated performance”.

    Possible further optimizations

    For debugging purposes the code was compiled with -g -O0 flags. While majority of the time was spent in backend functions (that was probably compiled with -O2 flags), the @@ -194,14 +196,14 @@ SQLite version: 3.7.9sourceid version is 2011-11-01 00:52:41 c7c6050ef060877ebe7 Multi-threaded or multi-process benchmark may be considered in the future. It may be somewhat difficult as only some backends support concurrent access. -

    Chapter 4. perfdhcp

    Purpose

    Evaluation of the performance of a DHCP server requires that it be tested under varying traffic loads. perfdhcp is a testing tool with the capability to create traffic loads and generate statistics from the results. Additional features, such as the ability to send customised DHCP packets, allow it to be used in a wide range of functional testing. -

    4.2. Key features

    +

    Key features

    perfdhcp has a number of command line switches to control DHCP message exchanges. Currently they fall into the following categories: @@ -234,7 +236,7 @@ SQLite version: 3.7.9sourceid version is 2011-11-01 00:52:41 c7c6050ef060877ebe7 IP layer options - specify the local/remote address, local interface and local port to be used for communication with DHCP server.

    -

    4.3. Command line options

    +

    Command line options

    The following "help" output from the tool describes the command line switches. This summary also lists the tool's possible exit codes as well as describing the @@ -362,7 +364,7 @@ The exit status is: exchanges are not successfully completed.

    -

    4.4. Starting perfdhcp

    +

    Starting perfdhcp

    In order to run a performance test, at least two separate systems have to be installed: client and server. The first one must have perfdhcp installed, and the latter must be running the DHCP server @@ -385,26 +387,22 @@ The exit status is: As DHCP uses low port numbers (67 for DHCPv4 relays and 547 for DHCPv6), running perfdhcp with non-root privileges will usually result in the error message similar to this: -

    $./perfdhcp -4 -l eth3 -r 100 all
     Error running perfdhcp: Failed to bind socket 3 to 172.16.1.2/port=67
             

    - The '-L' command line switch allows the use of a custom local port. However, although the following command line will work: -

    $./perfdhcp -4 -l eth3 -r 100 -L 10067 all

    - in the standard configuration no responses will be received from the DHCP server because the server responds to default relay port 67. A way to overcome this issue is to run perfdhcp as root. -

    4.5. perfdhcp command line examples

    +

    perfdhcp command line examples

    In this section, a number of perfdhcp command line examples are presented as a quick start guide for new users. For the detailed list of command line options refer to - Section 4.3, “Command line options”. -

    4.5.1. Example: basic usage

    + the section called “Command line options”. +

    Example: basic usage

    If server is listening on interface with IPv4 address 172.16.1.1, the simplest perfdhcp command line will look like:

    #./perfdhcp 172.16.1.1
    @@ -480,7 +478,7 @@ collected packets: 0
               

    #./perfdhcp -l eth3

    or a local address assigned to it:

    #./perfdhcp -l 172.16.1.2

    -

    4.5.2. Example: rate control

    +

    Example: rate control

    In the examples above perfdhcp initiates new exchanges with a best effort rate. With this setting, many packets are expected to be dropped by the server due to performance limitations. In many cases though, it is @@ -552,19 +550,28 @@ std deviation: 5.876 ms collected packets: 0

    There are now no packet drops, confirming that the server is able to - handle a load of 100 leases/second. + handle a load of 100 leases/second. Note that the last parameter (-R 30) configures perfdhcp to simulate traffic from 30 distinct clients. -

    4.5.3. Example: templates

    +

    Example: templates

    By default the DHCP messages are formed with default options. With template files, it is possible to define a custom packet format. -

    +

    + The template file names are specified with -T<template-file> + command line option. This option can be specified zero, one or two times. + The first occurence of this option refers to DISCOVER or SOLICIT message + and the second occurence refers to REQUEST (DHCPv4 or DHCPv6) message. + If -T option occurs only once the DISCOVER or SOLICIT message will be + created from the template and the REQUEST message will be created + dynamically (without the template). Note that each template file + holds data for exactly one DHCP message type. Templates for multiple + message types must not be combined in the single file. The content in template files is encoded in hexadecimal format. perfdhcp forms the packet by replacing parts of the binary stream read from the file with variable data such as elapsed time, hardware address, DUID etc. The offsets where such variable data is placed is specific to the template file and have to be specified from the command line. Refer to - Section 4.3, “Command line options” to find out how to + the section called “Command line options” to find out how to specify offsets for particular options and fields. With the following command line the DHCPv6 SOLICIT and REQUEST packets will be formed from solicit.hex and request6.hex packets: @@ -602,4 +609,16 @@ collected packets: 0 REQUEST template

  • -S 22 - server id position in the REQUEST template

  • -I 40 - IA_NA option position in the REQUEST template

  • -

    +

    + The offsets of options indicate where they begin in the packet. + The only exception from this rule is -O<random-offset> + option that specifies the end of the DUID (DHCPv6) or MAC address + (DHCPv4). Depending on the number of simulated clients + (see -R<random-range> command line option) perfdhcp + will be randomizing bytes in packet buffer starting from this + position backwards. For the number of simulated clients + <= 256 only one octet (at random-offset position) + will be ranomized, for the number of clients <= 65536 + two octets (at random-offset and random-offset-1) + will be randmized etc. +

    \ No newline at end of file diff --git a/tests/tools/dhcp-ubench/dhcp-perf-guide.xml b/tests/tools/dhcp-ubench/dhcp-perf-guide.xml index 2b7e00318f..cbd40d9cbe 100644 --- a/tests/tools/dhcp-ubench/dhcp-perf-guide.xml +++ b/tests/tools/dhcp-ubench/dhcp-perf-guide.xml @@ -815,16 +815,12 @@ The exit status is: As DHCP uses low port numbers (67 for DHCPv4 relays and 547 for DHCPv6), running perfdhcp with non-root privileges will usually result in the error message similar to this: - $./perfdhcp -4 -l eth3 -r 100 all Error running perfdhcp: Failed to bind socket 3 to 172.16.1.2/port=67 - The '-L' command line switch allows the use of a custom local port. However, although the following command line will work: - $./perfdhcp -4 -l eth3 -r 100 -L 10067 all - in the standard configuration no responses will be received from the DHCP server because the server responds to default relay port 67. A way to overcome this issue is to run @@ -1007,7 +1003,7 @@ std deviation: 5.876 ms collected packets: 0 There are now no packet drops, confirming that the server is able to - handle a load of 100 leases/second. + handle a load of 100 leases/second. Note that the last parameter (-R 30) configures perfdhcp to simulate traffic from 30 distinct clients. @@ -1017,8 +1013,17 @@ collected packets: 0 By default the DHCP messages are formed with default options. With template files, it is possible to define a custom packet format. - - + + + The template file names are specified with ]]> + command line option. This option can be specified zero, one or two times. + The first occurence of this option refers to DISCOVER or SOLICIT message + and the second occurence refers to REQUEST (DHCPv4 or DHCPv6) message. + If -T option occurs only once the DISCOVER or SOLICIT message will be + created from the template and the REQUEST message will be created + dynamically (without the template). Note that each template file + holds data for exactly one DHCP message type. Templates for multiple + message types must not be combined in the single file. The content in template files is encoded in hexadecimal format. perfdhcp forms the packet by replacing parts of the binary stream read from the file with variable data such as elapsed time, hardware address, DUID @@ -1068,6 +1073,19 @@ collected packets: 0 template + + The offsets of options indicate where they begin in the packet. + The only exception from this rule is ]]> + option that specifies the end of the DUID (DHCPv6) or MAC address + (DHCPv4). Depending on the number of simulated clients + (see ]]> command line option) perfdhcp + will be randomizing bytes in packet buffer starting from this + position backwards. For the number of simulated clients + 256 only one octet (at random-offset position) + will be ranomized, for the number of clients 65536 + two octets (at random-offset and random-offset-1) + will be randmized etc. +
    From 440fdf2334a3db842fedd3267c98e453b057bf66 Mon Sep 17 00:00:00 2001 From: Marcin Siodelski Date: Thu, 13 Sep 2012 11:02:23 +0200 Subject: [PATCH 155/199] [2230] Add perfdhcp internals manual as Doxygen file --- doc/devel/mainpage.dox | 3 +- tests/tools/perfdhcp/Makefile.am | 3 + tests/tools/perfdhcp/perfdhcp-classes.html | 126 ------------ tests/tools/perfdhcp/perfdhcp-classes.xml | 216 -------------------- tests/tools/perfdhcp/perfdhcp_internals.dox | 172 ++++++++++++++++ 5 files changed, 177 insertions(+), 343 deletions(-) delete mode 100644 tests/tools/perfdhcp/perfdhcp-classes.html delete mode 100644 tests/tools/perfdhcp/perfdhcp-classes.xml create mode 100644 tests/tools/perfdhcp/perfdhcp_internals.dox diff --git a/doc/devel/mainpage.dox b/doc/devel/mainpage.dox index a7fd094152..ca9d8815fa 100644 --- a/doc/devel/mainpage.dox +++ b/doc/devel/mainpage.dox @@ -24,6 +24,7 @@ * - @subpage libdhcp * - @subpage libdhcpIntro * - @subpage libdhcpIfaceMgr + * - @subpage perfdhcpInternals * * @section misc Miscellaneous topics * - @subpage LoggingApi @@ -36,4 +37,4 @@ * @todo: Move this logo to the right (and possibly up). Not sure what * is the best way to do it in Doxygen, without using CSS hacks. * @image html isc-logo.png - */ \ No newline at end of file + */ diff --git a/tests/tools/perfdhcp/Makefile.am b/tests/tools/perfdhcp/Makefile.am index 7c8064ea14..d6e8c36797 100644 --- a/tests/tools/perfdhcp/Makefile.am +++ b/tests/tools/perfdhcp/Makefile.am @@ -40,3 +40,6 @@ libb10_perfdhcp___la_LIBADD += $(top_builddir)/src/lib/asiolink/libb10-asiolink. pkglibexec_PROGRAMS = perfdhcp perfdhcp_SOURCES = perfdhcp.c + +# ... and the documentation +EXTRA_DIST = perfdhcp_internals.dox diff --git a/tests/tools/perfdhcp/perfdhcp-classes.html b/tests/tools/perfdhcp/perfdhcp-classes.html deleted file mode 100644 index d4dd2e5a9d..0000000000 --- a/tests/tools/perfdhcp/perfdhcp-classes.html +++ /dev/null @@ -1,126 +0,0 @@ - - - perfdhcp objects breakdown

    perfdhcp objects breakdown

    Marcin Siodelski

    This is a companion document for BIND 10 version - 20120817.

    Abstract

    This document briefly describes C++ classes being part of the - perfdhcp tool.


    Chapter 1. Introduction

    - The source code of perfdhcp tool has been refactored from C to C++. - There is a bunch of new C++ classes. in the refactored code. - All of them are well documented in Doxygen. This document is - limited to brief overview of major classes to help understand - their purpose. -

    Chapter 2. Classes

    CommandOptions (command_options.h .cc)

    - CommandOptions is a singleton class that parses perfdhcp command line - parameters and initializes its members accordingly. If parsed parameters - are invalid the parser function throws exception. -

    -main(int argc, char* argv[]) {
    -try {
    -    CommandOptions& command_options = CommandOptions::instance();
    -    command_options.parse(argc, argv);
    -catch(const Exception& e) {
    -    ...
    -}
    -...
    -}
    -        

    - If argument parsing is successful than parsed values can be read from - CommandOptions singleton from any class or function in the program: -

    -
    -...
    -int rate = CommandOptions::instance().getRate();
    -...
    -
    -        

    -

    TestControl (test_control.h .cc)

    - TestControl singleton is responsible for test execution coordination. - It relies on CommandOptions object to get all required test parameters. For - this reason CommandOptions has to be initialized and CommandOptions::parse() - has to be called prior to calling TestControl::run(). The latter function - performs initialization of TestControl internals and execues the main program - loop. The TestControl::run() function performs the following major operations: -

    1. check if command line has been parsed,

    2. prints diagnostics if specified from command line,

    3. register DHCP options factory functions,

    4. read packet templates from files,

    5. initialize Statistics Manager object,

    6. set interrupt signal handler (handle ^C),

    7. open and close socket for communication with server,

    8. coordinate sending and receiving packets,

    9. coordinate intermediate reporting,

    10. prints test statistics.

    -

    - TestControl is a singleton object so there is one sole instance of - it throughout the program. In order to allow running performance test - multiple times (using different command line options) with single instance - of the TestControl object it uses TestControl::reset() function internally - to reset state of the class members. Also, functions that initialize - various class members like Statistics Manager will release any objects - existing from previous test runs. -

    StatsMgr (stats_mgr.h)

    - StatsMgr is a class that holds all performance statistics gathered throughout - the test execution. Statistics Manager is created in TestControl. The - TestControl class posts all sent and received packets to StatsMgr. - Collected packets are used to match packets received from the server with - corresponding sent packets, calculate round trip time, number of packet - drops etc. Apart from the standard counters implemented in StatsMgr, - custom (named) counters can be specified and incremented by the calling - class. StatsMgr also exposes multiple functions that print gathered - statistics into the console. -

    - The StatsMgr is a template class that takes Pkt4, Pkt6, PerfPkt4 or - PerfPkt6 as a typename. The instance of the StatsMgr can be created - as follows: -

    -
    -typedef StatsMgr<Pkt4> StatsMgr4;
    -StatsMgr4 stats_mgr4 = boost::shared_ptr<StatsMgr4>(new StatsMgr4());
    -try {
    -    stats_mgr->addExchangeStats(StatsMgr4::XCHG_DO);
    -} catch(const Exception& e) {
    -    std::cout << e.what() << std::endl;
    -}
    -
    -        

    - The StatsMgr instance created in the example above will be used for - DHCPv4 testing (collect DHCPv4 packets) and will be configured to - monitor statistics for DISCOVER-OFFER packet exchanges. -

    PerfPkt4, PerfPkt6 (perf_pkt4.h .cc, perf_pkt6.h .cc)

    - The PerfPkt4 and PerfPkt6 classes are derived from dhcp::Pkt4 and - dhcp::Pkt6 (src/lib/dhcp). They extend parent class functionality - by adding support for template files. The instance of these classes - can be created using raw buffer (read from packet template file). - Once packet object is initialized it is possible to replace - parts of the on-wire data by using LocalizedOption mechanism. -

    LocalizedOption (localized_option.h)

    - LocalizedOption derives from dhcp::Option class. It represents the - DHCP option (v4 or v6) to be placed at specified position in the - packet buffer (see the section called “PerfPkt4, PerfPkt6 (perf_pkt4.h .cc, perf_pkt6.h .cc)”). Such option is added - to option collection in PerfPkt4 or PerfPkt6 object and when any of - PerfPktX::rawPack() function is called their content is stored in - the packet output buffer at the position pointed to by - LocalizedOption object. The LocalizedOption also allows to read the - on wire data in received packet at the specified position. In this - case LocalizedOption has to be created and added to recived packet. - When PerfPktX::rawUnpack() is called the contents of the buffer - will be read and stored in LocalizedOption object for further - processing. -

    - The following code shows how to create a packet from (template) - buffer and replace option data in the buffer with LocalizedOption. -

    -
    -OptionBuffer buf;
    -// fill buf with data here.
    -...
    -boost::scoped_ptr<PerfPkt4> pkt(new PerfPkt4(&buf[0], buf.size());
    -const size_t offset_hostname = 240;
    -OptionBuffer vec_hostname;
    -// fill the hostname vector with data
    -...
    -LocalizedOptionPtr opt_hostname(new LocalizedOption(Option::V4, DHO_HOST_NAME, vec_hostname,
    -                                                    offset_hostname));
    -pkt->addOption(opt_hostname);
    -// by calling rawPack() we replace packet contents with option contents at
    -// buffer position 240.
    -pkt->rawPack();
    -
    -        

    -

    PktTransform (pkt_transform.h .cc)

    - This helper class contains the static functions to pack and unpack - DHCP options (specifically LocalizedOption) to and from the packet - buffer. This logic has been moved away from PerfPkt4 and PerfPkt6 - classes to PktTransform because PerfPktX classes share the logic - here. -

    \ No newline at end of file diff --git a/tests/tools/perfdhcp/perfdhcp-classes.xml b/tests/tools/perfdhcp/perfdhcp-classes.xml deleted file mode 100644 index 8dda8b72a4..0000000000 --- a/tests/tools/perfdhcp/perfdhcp-classes.xml +++ /dev/null @@ -1,216 +0,0 @@ - - - -%version; -]> - - - - - - - - perfdhcp objects breakdown - - - - 2012 - Internet Systems Consortium, Inc. ("ISC") - - - Marcin - Siodelski - - - This document briefly describes C++ classes being part of the - perfdhcp tool. - - - This is a companion document for BIND 10 version - &__VERSION__;. - - - - - Introduction - - The source code of perfdhcp tool has been refactored from C to C++. - There is a bunch of new C++ classes. in the refactored code. - All of them are well documented in Doxygen. This document is - limited to brief overview of major classes to help understand - their purpose. - - - - Classes -
    - CommandOptions (command_options.h .cc) - - CommandOptions is a singleton class that parses perfdhcp command line - parameters and initializes its members accordingly. If parsed parameters - are invalid the parser function throws exception. - - - - If argument parsing is successful than parsed values can be read from - CommandOptions singleton from any class or function in the program: - - - - -
    -
    - TestControl (test_control.h .cc) - - TestControl singleton is responsible for test execution coordination. - It relies on CommandOptions object to get all required test parameters. For - this reason CommandOptions has to be initialized and CommandOptions::parse() - has to be called prior to calling TestControl::run(). The latter function - performs initialization of TestControl internals and execues the main program - loop. The TestControl::run() function performs the following major operations: - - check if command line has been parsed, - prints diagnostics if specified from command line, - register DHCP options factory functions, - read packet templates from files, - initialize Statistics Manager object, - set interrupt signal handler (handle ^C), - open and close socket for communication with server, - coordinate sending and receiving packets, - coordinate intermediate reporting, - prints test statistics. - - - - TestControl is a singleton object so there is one sole instance of - it throughout the program. In order to allow running performance test - multiple times (using different command line options) with single instance - of the TestControl object it uses TestControl::reset() function internally - to reset state of the class members. Also, functions that initialize - various class members like Statistics Manager will release any objects - existing from previous test runs. - -
    -
    - StatsMgr (stats_mgr.h) - - StatsMgr is a class that holds all performance statistics gathered throughout - the test execution. Statistics Manager is created in TestControl. The - TestControl class posts all sent and received packets to StatsMgr. - Collected packets are used to match packets received from the server with - corresponding sent packets, calculate round trip time, number of packet - drops etc. Apart from the standard counters implemented in StatsMgr, - custom (named) counters can be specified and incremented by the calling - class. StatsMgr also exposes multiple functions that print gathered - statistics into the console. - - - The StatsMgr is a template class that takes Pkt4, Pkt6, PerfPkt4 or - PerfPkt6 as a typename. The instance of the StatsMgr can be created - as follows: - - StatsMgr4; -StatsMgr4 stats_mgr4 = boost::shared_ptr(new StatsMgr4()); -try { - stats_mgr->addExchangeStats(StatsMgr4::XCHG_DO); -} catch(const Exception& e) { - std::cout << e.what() << std::endl; -} -]]> - - The StatsMgr instance created in the example above will be used for - DHCPv4 testing (collect DHCPv4 packets) and will be configured to - monitor statistics for DISCOVER-OFFER packet exchanges. - -
    -
    - PerfPkt4, PerfPkt6 (perf_pkt4.h .cc, perf_pkt6.h .cc) - - The PerfPkt4 and PerfPkt6 classes are derived from dhcp::Pkt4 and - dhcp::Pkt6 (src/lib/dhcp). They extend parent class functionality - by adding support for template files. The instance of these classes - can be created using raw buffer (read from packet template file). - Once packet object is initialized it is possible to replace - parts of the on-wire data by using LocalizedOption mechanism. - -
    -
    - LocalizedOption (localized_option.h) - - LocalizedOption derives from dhcp::Option class. It represents the - DHCP option (v4 or v6) to be placed at specified position in the - packet buffer (see ). Such option is added - to option collection in PerfPkt4 or PerfPkt6 object and when any of - PerfPktX::rawPack() function is called their content is stored in - the packet output buffer at the position pointed to by - LocalizedOption object. The LocalizedOption also allows to read the - on wire data in received packet at the specified position. In this - case LocalizedOption has to be created and added to recived packet. - When PerfPktX::rawUnpack() is called the contents of the buffer - will be read and stored in LocalizedOption object for further - processing. - - - The following code shows how to create a packet from (template) - buffer and replace option data in the buffer with LocalizedOption. - - pkt(new PerfPkt4(&buf[0], buf.size()); -const size_t offset_hostname = 240; -OptionBuffer vec_hostname; -// fill the hostname vector with data -... -LocalizedOptionPtr opt_hostname(new LocalizedOption(Option::V4, DHO_HOST_NAME, vec_hostname, - offset_hostname)); -pkt->addOption(opt_hostname); -// by calling rawPack() we replace packet contents with option contents at -// buffer position 240. -pkt->rawPack(); -]]> - - -
    -
    - PktTransform (pkt_transform.h .cc) - - This helper class contains the static functions to pack and unpack - DHCP options (specifically LocalizedOption) to and from the packet - buffer. This logic has been moved away from PerfPkt4 and PerfPkt6 - classes to PktTransform because PerfPktX classes share the logic - here. - -
    -
    -
    diff --git a/tests/tools/perfdhcp/perfdhcp_internals.dox b/tests/tools/perfdhcp/perfdhcp_internals.dox new file mode 100644 index 0000000000..c5a39c8715 --- /dev/null +++ b/tests/tools/perfdhcp/perfdhcp_internals.dox @@ -0,0 +1,172 @@ +// Copyright (C) 2012 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. + +/// @namespace perfdhcp +/// @page perfdhcpInternals perfdhcp Internals +/// +/// The perfdhcp utility provides a way of measuring the performance of +/// DHCP servers by generating large amounts of traffic. Written in C++, +/// its use is described in detail in the DHCP Performance Guide. +/// +/// This document is aimed at people wishing to understand the internals +/// of the perfdhcp program. It describes the major components in the +/// utility and their interaction. +/// +/// @section perfdhcpClasses perfdhcp Classes +/// +/// @subsection perfdhcpCommandOptions CommandOptions (Command Options) +/// +/// isc::perfdhcp::CommandOptions is a singleton class that parses +/// the perfdhcp command line parameters and initializes its members +/// accordingly. If the parameters are invalid, the parser method throws +/// an exception. Usage is simple: +/// +/// @code int main(int argc, char* argv[]) { +/// try { +/// CommandOptions& command_options = CommandOptions::instance(); +/// command_options.parse(argc, argv); +/// } catch(const Exception& e) { +/// ... +/// } +/// @endcode +/// +/// If the argument parsing is successful, the parsed values can be read +/// from the isc::perfdhcp::CommandOptions singleton from any class or +/// function in the program, e.g. +/// +/// @code +/// int rate = CommandOptions::instance().getRate(); +/// @endcode +/// +/// @subsection perfdhcpTestControl TestControl (Test Control) +/// +/// The isc::perfdhcp::TestControl singleton is responsible +/// for test execution coordination. It relies on the +/// isc::perfdhcp::CommandOptions object to get all required test +/// parameters and for this reason, isc::perfdhcp::CommandOptions has +/// to be initialized and isc::perfdhcp::CommandOptions::parse() called +/// prior to calling isc::perfdhcp::TestControl::run(). +/// +/// isc::perfdhcp::TestControl::run() performs initialization of +/// isc::perfdhcp::TestControl then executes the main program loop. In +/// detail, isc::perfdhcp::TestControl::run() performs the following +/// major operations: +/// +/// -# check if the command line has been parsed, +/// -# print diagnostics if specified from command line, +/// -# register DHCP options factory functions, +/// -# read packet templates from files, +/// -# initialize the isc::perfdhcp::StatisticsManager object, +/// -# set interrupt signal handler (handle ^C), +/// -# open and close socket for communication with server, +/// -# coordinate sending and receiving packets, +/// -# coordinate intermediate reporting, +/// -# prints test statistics. +/// +/// isc::perfdhcp::TestControl is a singleton object, so there is one sole +/// instance of it throughout the program. In order to allow the running +/// of unit tests, where a single instance of isc::perfdhcp::TestControl +/// is used multiple times with different command line options, a +/// isc::perfdhcp::TestControl::reset() function is provided to reset +/// the state of the class members. Also, functions that initialize +/// various class members (such as Statistics Manager) will release +/// any objects from previous test runs. +/// +/// @subsection perfStatsMgr StatsMgr (Statistics Manager) +/// +/// isc::perfdhcp::StatsMgr is a class that holds all performance +/// statistics gathered throughout the test execution and is created +/// in isc::perfdhcp::TestControl. isc::perfdhcp::TestControl posts all +/// sent and received packets to isc::perfdhcp::StatsMgr: outgoing packets +/// are recorded and incoming packets are matched with the corresponding +/// outgoing packer to calculate calculate round trip time, number of +/// packet drops etc. Apart from the standard counters implemented in +/// isc::perfdhcp::StatsMgr, custom (named) counters can be specified and +/// incremented by the calling class. isc::perfdhcp::StatsMgr also exposes +/// multiple functions that print gathered statistics into the console. +/// +/// isc::perfdhcp::StatsMgr is a template class that takes an +/// isc::dhcp::Pkt4, isc::dhcp::Pkt6, isc::perfdhcp::PerfPkt4 +/// or isc::perfdhcp::PerfPkt6 as a typename. An instance of +/// isc::perfdhcp::StatsMgr can be created by: +/// +/// @code +/// typedef StatsMgr StatsMgr4; StatsMgr4 stats_mgr4 = +/// boost::shared_ptr(new StatsMgr4()); try { +/// stats_mgr->addExchangeStats(StatsMgr4::XCHG_DO); +/// } catch(const Exception& e) { +/// std::cout << e.what() << std::endl; +/// } +/// @endcode +/// +/// The isc::perfdhcp::StatsMgr instance created in the example above will be used +/// for DHCPv4 testing (i.e. to collect DHCPv4 packets) and will be +/// configured to monitor statistics for DISCOVER-OFFER packet exchanges. +/// +/// @subsection perfdhcpPkt PerfPkt4 and PerfPkt6 +/// +/// The isc::perfdhcp::PerfPkt4 and isc::perfdhcp::PerfPkt6 classes +/// are derived from isc::dhcp::Pkt4 and isc::dhcp::Pkt6. They extend +/// the parent class functionality by adding support for template +/// files. Instances of these classes can be created using a raw buffer +/// (read from a packet template file). Once the packet object is +/// initialized, it is possible to replace parts of the on-wire data by +/// using the isc::perfdhcp::LocalizedOption mechanism. +/// +/// @subsection perfdhcpLocalizedOption LocalizedOption (Localized Option) +/// +/// isc::perfdhcp::LocalizedOption derives from the isc::dhcp::Option +/// class. It represents the DHCP option (v4 or v6) to be +/// placed at specified position in the packet buffer (see @ref +/// perfdhcpPkt). Such an option is added to the option collection in +/// a isc::perfdhcp::PerfPkt4 or isc::perfdhcp::PerfPkt6 object; when +/// any of PerfPktX::rawPack() functions are called their content is +/// stored in the packet output buffer at the position pointed to by +/// the isc::perfdhcp::LocalizedOption object. +/// +/// isc::perfdhcp::LocalizedOption also allows the reading of the +/// on wire data in received packet at the specified position. In +/// this case, isc::perfdhcp::LocalizedOption has to be created and +/// added to the received packet. When PerfPktX::rawUnpack() is +/// called, the contents of the buffer will be read and stored in a +/// isc::perfdhcp::LocalizedOption object for further processing. +/// +/// The following code shows how to create a packet from a +/// (template) buffer and replace option data in the buffer with +/// isc::perfdhcp::LocalizedOption. +/// +/// @code +/// OptionBuffer buf; // fill buf with data here. ... +/// boost::scoped_ptr pkt(new PerfPkt4(&buf[0], buf.size()); +/// const size_t offset_hostname = 240; +/// OptionBuffer vec_hostname; +/// // fill the hostname vector with data ... +/// LocalizedOptionPtr opt_hostname(new LocalizedOption(Option::V4, +/// DHO_HOST_NAME, +/// vec_hostname, +/// offset_hostname)); +/// pkt->addOption(opt_hostname); +/// // by calling rawPack() we replace the packet contents with option +/// // contents at buffer position 240. +/// pkt->rawPack(); +/// @endcode +/// +/// @subsection perfdhcpPktTransform PktTransform (Packet Transform) +/// +/// The isc::perfdhcp::PktTransform helper class contains the +/// static functions to pack and unpack DHCP options (specifically +/// isc::perfdhcp::LocalizedOption) to and from the packet buffer. This +/// logic has been moved away from isc::perfdhcp::PerfPkt4 and +/// isc::perfdhcp::PerfPkt6 classes to isc::perfdhcp::PktTransform +/// because PerfPktX classes share the logic here. From 34238640001954f7fae9bdd69401b6b3bd5ad225 Mon Sep 17 00:00:00 2001 From: Marcin Siodelski Date: Thu, 13 Sep 2012 11:20:44 +0200 Subject: [PATCH 156/199] [2230] Wording correction. --- tests/tools/dhcp-ubench/dhcp-perf-guide.html | 2 +- tests/tools/dhcp-ubench/dhcp-perf-guide.xml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/tools/dhcp-ubench/dhcp-perf-guide.html b/tests/tools/dhcp-ubench/dhcp-perf-guide.html index 34c3225513..1dd7ce5845 100644 --- a/tests/tools/dhcp-ubench/dhcp-perf-guide.html +++ b/tests/tools/dhcp-ubench/dhcp-perf-guide.html @@ -567,7 +567,7 @@ collected packets: 0 holds data for exactly one DHCP message type. Templates for multiple message types must not be combined in the single file. The content in template files is encoded in hexadecimal format. - perfdhcp forms the packet by replacing parts of the binary stream read + perfdhcp forms the packet by replacing parts of the message buffer read from the file with variable data such as elapsed time, hardware address, DUID etc. The offsets where such variable data is placed is specific to the template file and have to be specified from the command line. Refer to diff --git a/tests/tools/dhcp-ubench/dhcp-perf-guide.xml b/tests/tools/dhcp-ubench/dhcp-perf-guide.xml index cbd40d9cbe..cbf1bf65a4 100644 --- a/tests/tools/dhcp-ubench/dhcp-perf-guide.xml +++ b/tests/tools/dhcp-ubench/dhcp-perf-guide.xml @@ -1025,7 +1025,7 @@ collected packets: 0 holds data for exactly one DHCP message type. Templates for multiple message types must not be combined in the single file. The content in template files is encoded in hexadecimal format. - perfdhcp forms the packet by replacing parts of the binary stream read + perfdhcp forms the packet by replacing parts of the message buffer read from the file with variable data such as elapsed time, hardware address, DUID etc. The offsets where such variable data is placed is specific to the template file and have to be specified from the command line. Refer to From fc5c1f86c0af76912daa4a22d3dc6bece80726e4 Mon Sep 17 00:00:00 2001 From: Mukund Sivaraman Date: Thu, 13 Sep 2012 15:20:29 +0530 Subject: [PATCH 157/199] [master] Handle default switch case in LoggerImpl::outputRaw() --- src/lib/log/logger_impl.cc | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/lib/log/logger_impl.cc b/src/lib/log/logger_impl.cc index 6cd3da532e..689795d537 100644 --- a/src/lib/log/logger_impl.cc +++ b/src/lib/log/logger_impl.cc @@ -151,6 +151,15 @@ LoggerImpl::outputRaw(const Severity& severity, const string& message) { case FATAL: LOG4CPLUS_FATAL(logger_, message); + break; + + case NONE: + break; + + default: + LOG4CPLUS_ERROR(logger_, + "Unsupported severity in LoggerImpl::outputRaw(): " + << severity); } if (!locker.unlock()) { From 6a119e4ae98b2126f8f32ba4ad69ba98bd8a3144 Mon Sep 17 00:00:00 2001 From: Mukund Sivaraman Date: Thu, 13 Sep 2012 09:39:39 +0530 Subject: [PATCH 158/199] [master] Remove some unused assignments Reported by clang-analyzer. --- src/lib/log/tests/logger_example.cc | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/lib/log/tests/logger_example.cc b/src/lib/log/tests/logger_example.cc index 853d48a73f..6f95e5d1a3 100644 --- a/src/lib/log/tests/logger_example.cc +++ b/src/lib/log/tests/logger_example.cc @@ -146,7 +146,7 @@ int main(int argc, char** argv) { if (c_found || f_found || y_found) { cur_spec.addOutputOption(cur_opt); cur_opt = def_opt; - c_found = f_found = y_found = false; + f_found = y_found = false; } // Set the output option for this switch. @@ -174,7 +174,7 @@ int main(int argc, char** argv) { if (c_found || f_found || y_found) { cur_spec.addOutputOption(cur_opt); cur_opt = def_opt; - c_found = f_found = y_found = false; + c_found = y_found = false; } // Set the output option for this switch. @@ -236,7 +236,7 @@ int main(int argc, char** argv) { if (c_found || f_found || y_found) { cur_spec.addOutputOption(cur_opt); cur_opt = def_opt; - c_found = f_found = y_found = false; + c_found = f_found = false; } y_found = true; cur_opt.destination = OutputOption::DEST_SYSLOG; From ccdbcf02d9f4a2bbd69475428b86458c1ee6334a Mon Sep 17 00:00:00 2001 From: Mukund Sivaraman Date: Thu, 13 Sep 2012 15:22:22 +0530 Subject: [PATCH 159/199] [master] Remove redundant NULL checks in PySerial These are not required according to: http://docs.python.org/py3k/extending/extending.html?highlight=null#null-pointers These NULL checks cause static analysis reports in other parts of the code where the pointers are not NULL tested before use. --- src/lib/dns/python/serial_python.cc | 8 -------- 1 file changed, 8 deletions(-) diff --git a/src/lib/dns/python/serial_python.cc b/src/lib/dns/python/serial_python.cc index e2bd809219..f4f4720df5 100644 --- a/src/lib/dns/python/serial_python.cc +++ b/src/lib/dns/python/serial_python.cc @@ -259,19 +259,11 @@ createSerialObject(const Serial& source) { bool PySerial_Check(PyObject* obj) { - if (obj == NULL) { - isc_throw(PyCPPWrapperException, - "obj argument NULL in Serial typecheck"); - } return (PyObject_TypeCheck(obj, &serial_type)); } const Serial& PySerial_ToSerial(const PyObject* serial_obj) { - if (serial_obj == NULL) { - isc_throw(PyCPPWrapperException, - "obj argument NULL in Serial PyObject conversion"); - } const s_Serial* serial = static_cast(serial_obj); return (*serial->cppobj); } From e15c888265457e74ee5977659d6588a2d7a0d0e2 Mon Sep 17 00:00:00 2001 From: Mukund Sivaraman Date: Thu, 13 Sep 2012 10:02:23 +0530 Subject: [PATCH 160/199] [master] Add assertion to test that cmsg != NULL The assert() should silence clang-analyer. --- src/lib/dhcp/iface_mgr.cc | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/lib/dhcp/iface_mgr.cc b/src/lib/dhcp/iface_mgr.cc index 7df809e776..7f1b38ba8b 100644 --- a/src/lib/dhcp/iface_mgr.cc +++ b/src/lib/dhcp/iface_mgr.cc @@ -727,6 +727,13 @@ IfaceMgr::send(const Pkt6Ptr& pkt) { m.msg_control = &control_buf_[0]; m.msg_controllen = control_buf_len_; struct cmsghdr *cmsg = CMSG_FIRSTHDR(&m); + + // FIXME: Code below assumes that cmsg is not NULL, but + // CMSG_FIRSTHDR() is coded to return NULL as a possibility. The + // following assertion should never fail, but if it did and you came + // here, fix the code. :) + assert(cmsg != NULL); + cmsg->cmsg_level = IPPROTO_IPV6; cmsg->cmsg_type = IPV6_PKTINFO; cmsg->cmsg_len = CMSG_LEN(sizeof(struct in6_pktinfo)); From aea6e2d1a3e08c8239323dc076cdd9cc2620b09c Mon Sep 17 00:00:00 2001 From: Mukund Sivaraman Date: Thu, 13 Sep 2012 10:03:58 +0530 Subject: [PATCH 161/199] [master] Assign rcode Strictly, this is done to silence clang-analyzer. In every case, parseAnswer() does set rcode (which is passed by reference) unless it throws. It looks like clang-analyzer gets confused by the exception paths. In any case, it's good to initialize it to the unexpected case and not depend on parseAnswer(). --- src/lib/config/ccsession.cc | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/lib/config/ccsession.cc b/src/lib/config/ccsession.cc index d4c6653b6c..daec005dda 100644 --- a/src/lib/config/ccsession.cc +++ b/src/lib/config/ccsession.cc @@ -456,7 +456,7 @@ ModuleCCSession::ModuleCCSession( ConstElementPtr answer, env; session_.group_recvmsg(env, answer, false, seq); - int rcode; + int rcode = -1; ConstElementPtr err = parseAnswer(rcode, answer); if (rcode != 0) { LOG_ERROR(config_logger, CONFIG_MOD_SPEC_REJECT).arg(answer->str()); @@ -535,7 +535,7 @@ ModuleCCSession::handleConfigUpdate(ConstElementPtr new_config) { ConstElementPtr diff = removeIdentical(new_config, getLocalConfig()); // handle config update answer = config_handler_(diff); - int rcode; + int rcode = -1; parseAnswer(rcode, answer); if (rcode == 0) { ElementPtr local_config = getLocalConfig(); @@ -652,7 +652,7 @@ ModuleCCSession::fetchRemoteSpec(const std::string& module, bool is_filename) { unsigned int seq = session_.group_sendmsg(cmd, "ConfigManager"); ConstElementPtr env, answer; session_.group_recvmsg(env, answer, false, seq); - int rcode; + int rcode = -1; ConstElementPtr spec_data = parseAnswer(rcode, answer); if (rcode == 0 && spec_data) { // received OK, construct the spec out of it @@ -689,7 +689,7 @@ ModuleCCSession::addRemoteConfig(const std::string& spec_name, ConstElementPtr env, answer; session_.group_recvmsg(env, answer, false, seq); - int rcode; + int rcode = -1; ConstElementPtr new_config = parseAnswer(rcode, answer); ElementPtr local_config; if (rcode == 0 && new_config) { From b7598c1bf13043ba397c898849567ada04bbee9b Mon Sep 17 00:00:00 2001 From: Marcin Siodelski Date: Thu, 13 Sep 2012 14:25:42 +0200 Subject: [PATCH 162/199] [2231] Corrected validation ranges for timeouts. --- src/lib/dhcp/tests/iface_mgr_unittest.cc | 42 ++++++++---------------- 1 file changed, 14 insertions(+), 28 deletions(-) diff --git a/src/lib/dhcp/tests/iface_mgr_unittest.cc b/src/lib/dhcp/tests/iface_mgr_unittest.cc index adcba3152a..02dca323d9 100644 --- a/src/lib/dhcp/tests/iface_mgr_unittest.cc +++ b/src/lib/dhcp/tests/iface_mgr_unittest.cc @@ -233,31 +233,24 @@ TEST_F(IfaceMgrTest, receiveTimeout6) { // Socket is open if its descriptor is greater than zero. ASSERT_GT(socket1, 0); - // Time when call to IfaceMgr::receive6() started. - ptime start_time; - // Time when call to IfaceMgr::receive6() ended. - ptime stop_time; - // Time duration between start_time and stop_time. - time_duration duration; - // Remember when we call receive6(). - start_time = microsec_clock::universal_time(); + ptime start_time = microsec_clock::universal_time(); // Call receive with timeout of 1s + 400000us = 1.4s. Pkt6Ptr pkt; ASSERT_NO_THROW(pkt = ifacemgr->receive6(1, 400000)); // Remember when call to receive6() ended. - stop_time = microsec_clock::universal_time(); + ptime stop_time = microsec_clock::universal_time(); // We did not send a packet to lo interface so we expect that // nothing has been received and timeout has been reached. ASSERT_FALSE(pkt); // Calculate duration of call to receive6(). - duration = stop_time - start_time; + time_duration duration = stop_time - start_time; // We stop the clock when the call completes so it does not // precisely reflect the receive timeout. However the // uncertainity should be low enough to expect that measured - // value is in the range <1.4; 2). + // value is in the range <1.4s; 1.7s>. EXPECT_GE(duration.total_microseconds(), 1400000); - EXPECT_LT(duration.total_seconds(), 2); + EXPECT_LE(duration.total_microseconds(), 1700000); // Test timeout shorter than 1s. start_time = microsec_clock::universal_time(); @@ -265,9 +258,9 @@ TEST_F(IfaceMgrTest, receiveTimeout6) { stop_time = microsec_clock::universal_time(); ASSERT_FALSE(pkt); duration = stop_time - start_time; - // Check if measured duration is within <0.5s; 1s). + // Check if measured duration is within <0.5s; 0.8s>. EXPECT_GE(duration.total_microseconds(), 500000); - EXPECT_LT(duration.total_seconds(), 1); + EXPECT_LE(duration.total_microseconds(), 800000); // Test with invalid fractional timeout values. EXPECT_THROW(ifacemgr->receive6(0, 1000000), isc::BadValue); @@ -290,31 +283,24 @@ TEST_F(IfaceMgrTest, receiveTimeout4) { // Socket is open if its descriptor is greater than zero. ASSERT_GT(socket1, 0); - // Time when call to IfaceMgr::receive4() started. - ptime start_time; - // Time when call to IfaceMgr::receive4() ended. - ptime stop_time; - // Time duration between start_time and stop_time. - time_duration duration; - Pkt4Ptr pkt; // Remember when we call receive4(). - start_time = microsec_clock::universal_time(); + ptime start_time = microsec_clock::universal_time(); // Call receive with timeout of 2s + 300000us = 2.3s. ASSERT_NO_THROW(pkt = ifacemgr->receive4(2, 300000)); // Remember when call to receive4() ended. - stop_time = microsec_clock::universal_time(); + ptime stop_time = microsec_clock::universal_time(); // We did not send a packet to lo interface so we expect that // nothing has been received and timeout has been reached. ASSERT_FALSE(pkt); // Calculate duration of call to receive4(). - duration = stop_time - start_time; + time_duration duration = stop_time - start_time; // We stop the clock when the call completes so it does not // precisely reflect the receive timeout. However the // uncertainity should be low enough to expect that measured - // value is in the range <2.3s; 3s). + // value is in the range <2.3s; 2.6s>. EXPECT_GE(duration.total_microseconds(), 2300000); - EXPECT_LT(duration.total_seconds(), 3); + EXPECT_LE(duration.total_microseconds(), 2600000); // Test timeout shorter than 1s. start_time = microsec_clock::universal_time(); @@ -322,9 +308,9 @@ TEST_F(IfaceMgrTest, receiveTimeout4) { stop_time = microsec_clock::universal_time(); ASSERT_FALSE(pkt); duration = stop_time - start_time; - // Check if measured duration is within <0.4s; 1s). + // Check if measured duration is within <0.4s; 0.7s>. EXPECT_GE(duration.total_microseconds(), 400000); - EXPECT_LT(duration.total_seconds(), 1); + EXPECT_LE(duration.total_microseconds(), 700000); // Test with invalid fractional timeout values. EXPECT_THROW(ifacemgr->receive6(0, 1000000), isc::BadValue); From c47927ef079b47e10268c6677ea2494577fe462c Mon Sep 17 00:00:00 2001 From: Jelte Jansen Date: Thu, 13 Sep 2012 18:36:17 +0200 Subject: [PATCH 163/199] [2110] allow textToRRset to parse rrset+sigs --- src/lib/testutils/dnsmessage_test.cc | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/src/lib/testutils/dnsmessage_test.cc b/src/lib/testutils/dnsmessage_test.cc index ec6914d510..b166bb6fcd 100644 --- a/src/lib/testutils/dnsmessage_test.cc +++ b/src/lib/testutils/dnsmessage_test.cc @@ -91,10 +91,16 @@ matchRdata(const char*, const char*, void setRRset(RRsetPtr rrset, RRsetPtr* rrsetp) { if (*rrsetp) { - isc_throw(isc::Unexpected, - "multiple RRsets are given to textToRRset"); + // may be a sig + if (rrset->getType() == RRType::RRSIG()) { + (*rrsetp)->addRRsig(rrset); + } else { + isc_throw(isc::Unexpected, + "multiple RRsets are given to textToRRset"); + } + } else { + *rrsetp = rrset; } - *rrsetp = rrset; } } From 2a62124ab5a419a572a75aa77a0561732d5b11a6 Mon Sep 17 00:00:00 2001 From: Jelte Jansen Date: Thu, 13 Sep 2012 18:46:35 +0200 Subject: [PATCH 164/199] [2110] port wildcard handling to new inmem datasrc Including all relevant tests. The code makes use of the LabelSequence::WILDCARD as introduced in #2151, but otherwise it is mostly the same, with one big exception, it does not painstakingly recreate the renamed RRset name after wildcard substitution, but reuses the Name data as passed to the finder (since if the findNode found a wildcard when searching, the name to find should contain be the wildcard substitution) --- .../memory/tests/zone_finder_unittest.cc | 549 +++++++++++++++++- src/lib/datasrc/memory/zone_finder.cc | 99 +++- 2 files changed, 619 insertions(+), 29 deletions(-) diff --git a/src/lib/datasrc/memory/tests/zone_finder_unittest.cc b/src/lib/datasrc/memory/tests/zone_finder_unittest.cc index 15fec597d8..8f046a9a43 100644 --- a/src/lib/datasrc/memory/tests/zone_finder_unittest.cc +++ b/src/lib/datasrc/memory/tests/zone_finder_unittest.cc @@ -141,6 +141,10 @@ protected: ZoneFinder::FIND_DEFAULT); void emptyNodeCheck(ZoneFinder::FindResultFlags expected_flags = ZoneFinder::RESULT_DEFAULT); + void wildcardCheck(ZoneFinder::FindResultFlags expected_flags = + ZoneFinder::RESULT_DEFAULT, + ZoneFinder::FindOptions find_options = + ZoneFinder::FIND_DEFAULT); void doCancelWildcardCheck(ZoneFinder::FindResultFlags expected_flags = ZoneFinder::RESULT_DEFAULT, ZoneFinder::FindOptions find_options = @@ -256,6 +260,7 @@ public: if (old_data != NULL) { NSEC3Data::destroy(mem_sgmt_, old_data, rrset->getClass()); } + zone_data_->setSigned(true); } // simplified version of 'loading' data @@ -264,9 +269,11 @@ public: if (rrset->getType() == RRType::NSEC3()) { return (addZoneDataNSEC3(rrset)); + } else if (rrset->getType() == RRType::NSEC()) { + zone_data_->setSigned(true); } + zone_data_->insertName(mem_sgmt_, rrset->getName(), &node); - RdataSet* next_rds = node->getData(); if (rrset->getType() == RRType::NS() && rrset->getName() != zone_data_->getOriginNode()->getName()) { @@ -275,10 +282,28 @@ public: node->setFlag(DomainTreeNode::FLAG_CALLBACK); } + RdataSet* next_rds = node->getData(); RdataSet* rdataset = - RdataSet::create(mem_sgmt_, encoder_, rrset, ConstRRsetPtr()); + RdataSet::create(mem_sgmt_, encoder_, rrset, rrset->getRRsig()); rdataset->next = next_rds; node->setData(rdataset); + + // find wildcard nodes in name (go through all of them in case there + // is a nonterminal one) + // Note that this method is pretty much equal to the 'real' loader; + // but less efficient + Name name(rrset->getName()); + while (name.getLabelCount() > 1) { + if (name.isWildcard()) { + ZoneNode* wnode = NULL; + // add Wild node + zone_data_->insertName(mem_sgmt_, name.split(1), &wnode); + wnode->setFlag(ZoneData::WILDCARD_NODE); + // add wildcard name itself too + zone_data_->insertName(mem_sgmt_, name, &wnode); + } + name = name.split(1); + } } // Some data to test with @@ -399,11 +424,12 @@ public: ASSERT_FALSE(find_result->rrset); } else { ASSERT_TRUE(find_result->rrset); - rrsetCheck(answer, convertRRset(find_result->rrset)); + ConstRRsetPtr result_rrset( + convertRRset(find_result->rrset)); + rrsetCheck(answer, result_rrset); if (answer_sig) { - ASSERT_TRUE(find_result->rrset->getRRsig()); - rrsetCheck(answer_sig, - find_result->rrset->getRRsig()); + ASSERT_TRUE(result_rrset->getRRsig()); + rrsetCheck(answer_sig, result_rrset->getRRsig()); } } } else if (check_wild_answer) { @@ -421,11 +447,14 @@ public: for (; !expectedIt->isLast(); expectedIt->next()) { wildanswer->addRdata(expectedIt->getCurrent()); } - rrsetCheck(wildanswer, find_result->rrset); + + ConstRRsetPtr result_rrset( + convertRRset(find_result->rrset)); + rrsetCheck(wildanswer, result_rrset); // Same for the RRSIG, if any. if (answer_sig) { - ASSERT_TRUE(find_result->rrset->getRRsig()); + ASSERT_TRUE(result_rrset->getRRsig()); RRsetPtr wildsig(new RRset(name, answer_sig->getClass(), @@ -436,7 +465,7 @@ public: for (; !expectedIt->isLast(); expectedIt->next()) { wildsig->addRdata(expectedIt->getCurrent()); } - rrsetCheck(wildsig, find_result->rrset->getRRsig()); + rrsetCheck(wildsig, result_rrset->getRRsig()); } } }); @@ -745,6 +774,7 @@ InMemoryZoneFinderTest::findCheck(ZoneFinder::FindResultFlags expected_flags, if ((expected_flags & ZoneFinder::RESULT_NSEC_SIGNED) != 0) { addZoneData(rr_ns_nsec_); + zone_data_->setSigned(true); if ((find_options & ZoneFinder::FIND_DNSSEC) != 0) { expected_nsec = rr_ns_nsec_; } @@ -814,12 +844,12 @@ TEST_F(InMemoryZoneFinderTest,findNSECEmptyNonterminal) { findNSECENTCheck(Name("wild.example.org"), rr_ent_nsec3_); } -TEST_F(InMemoryZoneFinderTest, DISABLED_findNSECEmptyNonterminalWildcard) { +TEST_F(InMemoryZoneFinderTest, findNSECEmptyNonterminalWildcard) { // Wildcard case, above actual wildcard findNSECENTCheck(Name("foo.example.org"), rr_nsec_); } -TEST_F(InMemoryZoneFinderTest,DISABLED_findNSECEmptyNonterminalAtWildcard) { +TEST_F(InMemoryZoneFinderTest, findNSECEmptyNonterminalAtWildcard) { // Wildcard case, at actual wildcard findNSECENTCheck(Name("bar.foo.example.org"), rr_nsec_, ZoneFinder::RESULT_WILDCARD); @@ -894,6 +924,503 @@ TEST_F(InMemoryZoneFinderTest, emptyNodeNSEC) { emptyNodeCheck(ZoneFinder::RESULT_NSEC_SIGNED); } +/* + * Test that puts a (simple) wildcard into the zone and checks we can + * correctly find the data. + */ +void +InMemoryZoneFinderTest::wildcardCheck( + ZoneFinder::FindResultFlags expected_flags, + ZoneFinder::FindOptions find_options) +{ + /* + * example.org. + * | + * [cname]wild (not *.wild, should have wild mark) + * | + * * + */ + + // If the zone is "signed" (detecting it by the NSEC/NSEC3 signed flags), + // add RRSIGs to the records. + if ((expected_flags & ZoneFinder::RESULT_NSEC_SIGNED) != 0 || + (expected_flags & ZoneFinder::RESULT_NSEC3_SIGNED) != 0) { + // Convenience shortcut. The RDATA is not really validatable, but + // it doesn't matter for our tests. + const char* const rrsig_common = "5 3 3600 " + "20000101000000 20000201000000 12345 example.org. FAKEFAKEFAKE"; + + find_options = find_options | ZoneFinder::FIND_DNSSEC; + rr_wild_->addRRsig(textToRRset("*.wild.example.org. 300 IN RRSIG A " + + string(rrsig_common))); + rr_cnamewild_->addRRsig(textToRRset("*.cnamewild.example.org. 300 IN " + "RRSIG CNAME " + + string(rrsig_common))); + } + addZoneData(rr_wild_); + addZoneData(rr_cnamewild_); + // If the zone is expected to be "signed" with NSEC3, add an NSEC3. + // (the content of the NSEC3 shouldn't matter) + if ((expected_flags & ZoneFinder::RESULT_NSEC3_SIGNED) != 0) { + addZoneData(rr_nsec3_); + } + if ((expected_flags & ZoneFinder::RESULT_NSEC_SIGNED) != 0) { + addZoneData(rr_nsec_); + } + + // Search at the parent. The parent will not have the A, but it will + // be in the wildcard (so check the wildcard isn't matched at the parent) + { + SCOPED_TRACE("Search at parent"); + if ((expected_flags & ZoneFinder::RESULT_NSEC_SIGNED) != 0) { + findTest(Name("wild.example.org"), RRType::A(), + ZoneFinder::NXRRSET, true, rr_nsec_, expected_flags, + NULL, find_options); + } else { + findTest(Name("wild.example.org"), RRType::A(), + ZoneFinder::NXRRSET, true, ConstRRsetPtr(), + expected_flags, NULL, find_options); + } + } + + // For the test setup of "NSEC-signed" zone, we might expect it will + // be returned with a negative result, either because wildcard match is + // disabled by the search option or because wildcard match is canceled + // per protocol. + ConstRRsetPtr expected_nsec; // by default it's NULL + if ((expected_flags & ZoneFinder::RESULT_NSEC_SIGNED) != 0 && + (find_options & ZoneFinder::FIND_DNSSEC) != 0) { + expected_nsec = rr_nsec_; + } + // Explicitly converting the following to const pointers; some compilers + // would complain about mixed use of const and non const in ?: below. + const ConstRRsetPtr rr_wild = rr_wild_; + const ConstRRsetPtr rr_cnamewild = rr_cnamewild_; + + // Search the original name of wildcard + { + SCOPED_TRACE("Search directly at *"); + findTest(Name("*.wild.example.org"), RRType::A(), ZoneFinder::SUCCESS, + true, rr_wild_, ZoneFinder::RESULT_DEFAULT, NULL, + find_options); + } + + // Below some of the test cases will normally result in a wildcard match; + // if NO_WILDCARD is specified, it should result in NXDOMAIN instead, + // and, when available and requested, the covering NSEC will be returned. + // The following are shortcut parameters to unify these cases. + const bool wild_ok = ((find_options & ZoneFinder::NO_WILDCARD) == 0); + const ZoneFinder::FindResultFlags wild_expected_flags = + wild_ok ? (ZoneFinder::RESULT_WILDCARD | expected_flags) : + expected_flags; + + // Search "created" name. + { + SCOPED_TRACE("Search at created child"); + findTest(Name("a.wild.example.org"), RRType::A(), + wild_ok ? ZoneFinder::SUCCESS : ZoneFinder::NXDOMAIN, false, + wild_ok ? rr_wild : expected_nsec, + wild_expected_flags, NULL, find_options, wild_ok); + } + + // Search name that has CNAME. + { + SCOPED_TRACE("Matching CNAME"); + findTest(Name("a.cnamewild.example.org"), RRType::A(), + wild_ok ? ZoneFinder::CNAME : ZoneFinder::NXDOMAIN, false, + wild_ok ? rr_cnamewild : expected_nsec, + wild_expected_flags, NULL, find_options, wild_ok); + } + + // Search another created name, this time little bit lower + { + SCOPED_TRACE("Search at created grand-child"); + findTest(Name("a.b.wild.example.org"), RRType::A(), + wild_ok ? ZoneFinder::SUCCESS : ZoneFinder::NXDOMAIN, false, + wild_ok ? rr_wild : expected_nsec, + wild_expected_flags, NULL, find_options, wild_ok); + } + + addZoneData(rr_under_wild_); + { + SCOPED_TRACE("Search under non-wildcard"); + findTest(Name("bar.foo.wild.example.org"), RRType::A(), + ZoneFinder::NXDOMAIN, true, expected_nsec, expected_flags, + NULL, find_options); + } + + // Wildcard match, but no data. We add the additional NSEC at the wildcard + // at this point so that it wouldn't break other tests above. Note also + // that in the NO_WILDCARD case the resulting NSEC is the same. Ideally + // we could use a more tricky setup so we can distinguish these cases, + // but for this purpose it's not bad; what we'd like to test here is that + // wildcard substitution doesn't happen for either case, and the + // NO_WILDCARD effect itself can be checked by the result code (NXDOMAIN). + ConstRRsetPtr expected_wild_nsec; // by default it's NULL + if ((expected_flags & ZoneFinder::RESULT_NSEC_SIGNED) != 0) { + addZoneData(rr_wild_nsec_); + expected_wild_nsec = rr_wild_nsec_; + } + { + SCOPED_TRACE("Search at wildcard, no data"); + findTest(Name("a.wild.example.org"), RRType::AAAA(), + wild_ok ? ZoneFinder::NXRRSET : ZoneFinder::NXDOMAIN, true, + wild_ok ? expected_wild_nsec : expected_wild_nsec, + wild_expected_flags, NULL, find_options); + } +} + +TEST_F(InMemoryZoneFinderTest, wildcard) { + // Normal case + wildcardCheck(); +} + +TEST_F(InMemoryZoneFinderTest, wildcardDisabledWithNSEC) { + // Wildcard is disabled. In practice, this is used as part of query + // processing for an NSEC-signed zone, so we test that case specifically. + wildcardCheck(ZoneFinder::RESULT_NSEC_SIGNED, ZoneFinder::NO_WILDCARD); +} + +TEST_F(InMemoryZoneFinderTest, wildcardDisabledWithoutNSEC) { + // Similar to the previous once, but check the behavior for a non signed + // zone just in case. + wildcardCheck(ZoneFinder::RESULT_DEFAULT, ZoneFinder::NO_WILDCARD); +} + +/* + * Test that we don't match a wildcard if we get under delegation. + * By 4.3.3 of RFC1034: + * "Wildcard RRs do not apply: + * - When the query is in another zone. That is, delegation cancels + * the wildcard defaults." + */ +TEST_F(InMemoryZoneFinderTest, delegatedWildcard) { + addZoneData(rr_child_wild_); + addZoneData(rr_child_ns_); + + { + SCOPED_TRACE("Looking under delegation point"); + findTest(Name("a.child.example.org"), RRType::A(), + ZoneFinder::DELEGATION, true, rr_child_ns_); + } + + { + SCOPED_TRACE("Looking under delegation point in GLUE_OK mode"); + findTest(Name("a.child.example.org"), RRType::A(), + ZoneFinder::DELEGATION, true, rr_child_ns_, + ZoneFinder::RESULT_DEFAULT, NULL, ZoneFinder::FIND_GLUE_OK); + } +} + +// Tests combination of wildcard and ANY. +void +InMemoryZoneFinderTest::anyWildcardCheck( + ZoneFinder::FindResultFlags expected_flags) +{ + addZoneData(rr_wild_); + if ((expected_flags & ZoneFinder::RESULT_NSEC3_SIGNED) != 0) { + addZoneData(rr_nsec3_); + } + if ((expected_flags & ZoneFinder::RESULT_NSEC_SIGNED) != 0) { + addZoneData(rr_nsec_); + } + + vector expected_sets; + + // First try directly the name (normal match) + { + SCOPED_TRACE("Asking directly for *"); + expected_sets.push_back(rr_wild_); + findAllTest(Name("*.wild.example.org"), ZoneFinder::SUCCESS, + expected_sets); + } + + // Then a wildcard match + { + SCOPED_TRACE("Asking in the wild way"); + expected_sets.clear(); + RRsetPtr expected(new RRset(Name("a.wild.example.org"), + rr_wild_->getClass(), rr_wild_->getType(), + rr_wild_->getTTL())); + expected->addRdata(rr_wild_->getRdataIterator()->getCurrent()); + expected_sets.push_back(expected); + findAllTest(Name("a.wild.example.org"), ZoneFinder::SUCCESS, + expected_sets, + ZoneFinder::RESULT_WILDCARD | expected_flags); + } +} + +TEST_F(InMemoryZoneFinderTest, anyWildcard) { + anyWildcardCheck(); +} + +TEST_F(InMemoryZoneFinderTest, anyWildcardNSEC3) { + anyWildcardCheck(ZoneFinder::RESULT_NSEC3_SIGNED); +} + +TEST_F(InMemoryZoneFinderTest, anyWildcardNSEC) { + anyWildcardCheck(ZoneFinder::RESULT_NSEC_SIGNED); +} + +// Test there's nothing in the wildcard in the middle if we load +// wild.*.foo.example.org. +void +InMemoryZoneFinderTest::emptyWildcardCheck( + ZoneFinder::FindResultFlags expected_flags) +{ + /* + * example.org. + * foo + * * + * wild + */ + addZoneData(rr_emptywild_); + if ((expected_flags & ZoneFinder::RESULT_NSEC3_SIGNED) != 0) { + addZoneData(rr_nsec3_); + } + if ((expected_flags & ZoneFinder::RESULT_NSEC_SIGNED) != 0) { + addZoneData(rr_nsec_); + } + + { + SCOPED_TRACE("Asking for the original record under wildcard"); + findTest(Name("wild.*.foo.example.org"), RRType::A(), + ZoneFinder::SUCCESS, true, rr_emptywild_); + } + + { + SCOPED_TRACE("Asking for A record"); + findTest(Name("a.foo.example.org"), RRType::A(), ZoneFinder::NXRRSET, + true, ConstRRsetPtr(), + ZoneFinder::RESULT_WILDCARD | expected_flags); + findTest(Name("*.foo.example.org"), RRType::A(), ZoneFinder::NXRRSET, + true, ConstRRsetPtr(), expected_flags); + findTest(Name("foo.example.org"), RRType::A(), ZoneFinder::NXRRSET, + true, ConstRRsetPtr(), expected_flags); + } + + { + SCOPED_TRACE("Asking for ANY record"); + findAllTest(Name("*.foo.example.org"), ZoneFinder::NXRRSET, + vector(), expected_flags); + + findAllTest(Name("a.foo.example.org"), ZoneFinder::NXRRSET, + vector(), + ZoneFinder::RESULT_WILDCARD | expected_flags); + } + + { + SCOPED_TRACE("Asking on the non-terminal"); + findTest(Name("wild.bar.foo.example.org"), RRType::A(), + ZoneFinder::NXRRSET, true, ConstRRsetPtr(), + ZoneFinder::RESULT_WILDCARD | expected_flags); + } +} + +TEST_F(InMemoryZoneFinderTest, emptyWildcard) { + emptyWildcardCheck(); +} + +TEST_F(InMemoryZoneFinderTest, emptyWildcardNSEC3) { + emptyWildcardCheck(ZoneFinder::RESULT_NSEC3_SIGNED); +} + +TEST_F(InMemoryZoneFinderTest, emptyWildcardNSEC) { + emptyWildcardCheck(ZoneFinder::RESULT_NSEC_SIGNED); +} + +// Same as emptyWildcard, but with multiple * in the path. +TEST_F(InMemoryZoneFinderTest, nestedEmptyWildcard) { + addZoneData(rr_nested_emptywild_); + + { + SCOPED_TRACE("Asking for the original record under wildcards"); + findTest(Name("wild.*.foo.*.bar.example.org"), RRType::A(), + ZoneFinder::SUCCESS, true, rr_nested_emptywild_); + } + + { + SCOPED_TRACE("Matching wildcard against empty nonterminal"); + + const char* names[] = { + "baz.foo.*.bar.example.org", + "baz.foo.baz.bar.example.org", + "*.foo.baz.bar.example.org", + NULL + }; + + for (const char** name = names; *name != NULL; ++ name) { + SCOPED_TRACE(string("Node ") + *name); + findTest(Name(*name), RRType::A(), ZoneFinder::NXRRSET, true, + ConstRRsetPtr(), ZoneFinder::RESULT_WILDCARD); + } + } + + // Domains to test + const char* names[] = { + "*.foo.*.bar.example.org", + "foo.*.bar.example.org", + "*.bar.example.org", + "bar.example.org", + NULL + }; + + { + SCOPED_TRACE("Asking directly for A on parent nodes"); + + for (const char** name = names; *name != NULL; ++ name) { + SCOPED_TRACE(string("Node ") + *name); + findTest(Name(*name), RRType::A(), ZoneFinder::NXRRSET); + } + } + + { + SCOPED_TRACE("Asking for ANY on parent nodes"); + + for (const char** name = names; *name != NULL; ++ name) { + SCOPED_TRACE(string("Node ") + *name); + + findAllTest(Name(*name), ZoneFinder::NXRRSET, + vector()); + } + } +} + +// We run this part twice from the below test, in two slightly different +// situations +void +InMemoryZoneFinderTest::doCancelWildcardCheck( + ZoneFinder::FindResultFlags expected_flags, + ZoneFinder::FindOptions find_options) +{ + // These should be canceled + { + SCOPED_TRACE("Canceled under foo.wild.example.org"); + + // For an NSEC-signed zone with DNSSEC requested, the covering NSEC + // should be returned. The expected NSEC is actually just the only + // NSEC in the test data, but in this context it doesn't matter; + // it's sufficient just to check any NSEC is returned (or not). + ConstRRsetPtr expected_nsec; // by default it's NULL + if ((expected_flags & ZoneFinder::RESULT_NSEC_SIGNED) != 0 && + (find_options & ZoneFinder::FIND_DNSSEC)) { + expected_nsec = rr_nsec_; + } + + findTest(Name("aaa.foo.wild.example.org"), RRType::A(), + ZoneFinder::NXDOMAIN, true, expected_nsec, expected_flags, + NULL, find_options); + findTest(Name("zzz.foo.wild.example.org"), RRType::A(), + ZoneFinder::NXDOMAIN, true, expected_nsec, expected_flags, + NULL, find_options); + } + + // This is existing, non-wildcard domain, shouldn't wildcard at all + { + SCOPED_TRACE("Existing domain under foo.wild.example.org"); + findTest(Name("bar.foo.wild.example.org"), RRType::A(), + ZoneFinder::SUCCESS, true, rr_not_wild_); + } + + // These should be caught by the wildcard + { + SCOPED_TRACE("Neighbor wildcards to foo.wild.example.org"); + + const char* names[] = { + "aaa.bbb.wild.example.org", + "aaa.zzz.wild.example.org", + "zzz.wild.example.org", + NULL + }; + + for (const char** name = names; *name != NULL; ++ name) { + SCOPED_TRACE(string("Node ") + *name); + + findTest(Name(*name), RRType::A(), ZoneFinder::SUCCESS, false, + rr_wild_, + ZoneFinder::RESULT_WILDCARD | expected_flags, NULL, + ZoneFinder::FIND_DEFAULT, true); + } + } + + // This shouldn't be wildcarded, it's an existing domain + { + SCOPED_TRACE("The foo.wild.example.org itself"); + findTest(Name("foo.wild.example.org"), RRType::A(), + ZoneFinder::NXRRSET, true, ConstRRsetPtr(), expected_flags); + } +} + +/* + * This tests that if there's a name between the wildcard domain and the + * searched one, it will not trigger wildcard, for example, if we have + * *.wild.example.org and bar.foo.wild.example.org, then we know + * foo.wild.example.org exists and is not wildcard. Therefore, search for + * aaa.foo.wild.example.org should return NXDOMAIN. + * + * Tests few cases "around" the canceled wildcard match, to see something that + * shouldn't be canceled isn't. + */ +TEST_F(InMemoryZoneFinderTest, cancelWildcard) { + addZoneData(rr_wild_); + addZoneData(rr_not_wild_); + + { + SCOPED_TRACE("Runnig with single entry under foo.wild.example.org"); + doCancelWildcardCheck(); + } + + // Try putting another one under foo.wild.... + // The result should be the same but it will be done in another way in the + // code, because the foo.wild.example.org will exist in the tree. + addZoneData(rr_not_wild_another_); + { + SCOPED_TRACE("Runnig with two entries under foo.wild.example.org"); + doCancelWildcardCheck(); + } +} + +// Same tests as cancelWildcard for NSEC3-signed zone +TEST_F(InMemoryZoneFinderTest, cancelWildcardNSEC3) { + addZoneData(rr_wild_); + addZoneData(rr_not_wild_); + addZoneData(rr_nsec3_); + + { + SCOPED_TRACE("Runnig with single entry under foo.wild.example.org"); + doCancelWildcardCheck(ZoneFinder::RESULT_NSEC3_SIGNED); + } + addZoneData(rr_not_wild_another_); + { + SCOPED_TRACE("Runnig with two entries under foo.wild.example.org"); + doCancelWildcardCheck(ZoneFinder::RESULT_NSEC3_SIGNED); + } +} + +// Same tests as cancelWildcard for NSEC-signed zone. Check both cases with +// or without FIND_DNSSEC option. NSEC should be returned only when the option +// is given. +TEST_F(InMemoryZoneFinderTest, cancelWildcardNSEC) { + addZoneData(rr_wild_); + addZoneData(rr_not_wild_); + addZoneData(rr_nsec_); + + { + SCOPED_TRACE("Runnig with single entry under foo.wild.example.org"); + doCancelWildcardCheck(ZoneFinder::RESULT_NSEC_SIGNED, + ZoneFinder::FIND_DNSSEC); + doCancelWildcardCheck(ZoneFinder::RESULT_NSEC_SIGNED); + } + addZoneData(rr_not_wild_another_); + { + SCOPED_TRACE("Runnig with two entries under foo.wild.example.org"); + doCancelWildcardCheck(ZoneFinder::RESULT_NSEC_SIGNED, + ZoneFinder::FIND_DNSSEC); + doCancelWildcardCheck(ZoneFinder::RESULT_NSEC_SIGNED); + } +} + + // DISABLED: nsec3 will be re-added in #2118 TEST_F(InMemoryZoneFinderTest, DISABLED_findNSEC3) { // Set up the faked hash calculator. diff --git a/src/lib/datasrc/memory/zone_finder.cc b/src/lib/datasrc/memory/zone_finder.cc index 994b0f98f3..f9aa027cdc 100644 --- a/src/lib/datasrc/memory/zone_finder.cc +++ b/src/lib/datasrc/memory/zone_finder.cc @@ -43,16 +43,24 @@ namespace { /// \param node The ZoneNode found by the find() calls /// \param rdataset The RdataSet to create the RRsetPtr for /// \param rrclass The RRClass as passed by the client +/// \param realname If given, the TreeNodeRRset is created with this name +/// (e.g. for wildcard substitution) /// /// Returns an empty TreeNodeRRsetPtr is either node or rdataset is NULL. TreeNodeRRsetPtr createTreeNodeRRset(const ZoneNode* node, const RdataSet* rdataset, - const RRClass& rrclass) + const RRClass& rrclass, + const Name* realname = NULL) { if (node != NULL && rdataset != NULL) { - return TreeNodeRRsetPtr(new TreeNodeRRset(rrclass, node, - rdataset, true)); + if (realname != NULL) { + return TreeNodeRRsetPtr(new TreeNodeRRset(*realname, rrclass, node, + rdataset, true)); + } else { + return TreeNodeRRsetPtr(new TreeNodeRRset(rrclass, node, + rdataset, true)); + } } else { return TreeNodeRRsetPtr(); } @@ -147,17 +155,24 @@ bool cutCallback(const ZoneNode& node, FindState* state) { // // Also performs the conversion of node + RdataSet into a TreeNodeRRsetPtr // +// if wild is true, the RESULT_WILDCARD flag will be set. +// If qname is not NULL, this is the query name, to be used in wildcard +// substitution instead of the Node's name). isc::datasrc::memory::ZoneFinderResultContext createFindResult(const RRClass& rrclass, const ZoneData& zone_data, ZoneFinder::Result code, const RdataSet* rrset, const ZoneNode* node, - bool wild = false) { + bool wild = false, + const Name* qname = NULL) { ZoneFinder::FindResultFlags flags = ZoneFinder::RESULT_DEFAULT; + const Name* rename = NULL; if (wild) { flags = flags | ZoneFinder::RESULT_WILDCARD; + // only use the rename qname if wild is true + rename = qname; } if (code == ZoneFinder::NXRRSET || code == ZoneFinder::NXDOMAIN || wild) { if (zone_data.isNSEC3Signed()) { @@ -167,9 +182,8 @@ createFindResult(const RRClass& rrclass, } } - return (ZoneFinderResultContext(code, createTreeNodeRRset(node, - rrset, - rrclass), + return (ZoneFinderResultContext(code, createTreeNodeRRset(node, rrset, + rrclass, rename), flags, node)); } @@ -297,7 +311,6 @@ public: // non-terminal node. In this case the search name is considered to exist // but no data should be found there. // -// (TODO: check this part when doing #2110) // If none of above is the case, we then consider whether there's a matching // wildcard. DomainTree::find() records the node if it encounters a // "wildcarding" node, i.e., the immediate ancestor of a wildcard name @@ -373,8 +386,51 @@ FindNodeResult findNode(const ZoneData& zone_data, return (FindNodeResult(ZoneFinder::NXRRSET, nsec_node, nsec_rds)); } - // TODO: wildcard (see memory_datasrc.cc:480 and ticket #2110) // Nothing really matched. + + // May be a wildcard, but check only if not disabled + if (node->getFlag(ZoneData::WILDCARD_NODE) && + (options & ZoneFinder::NO_WILDCARD) == 0) { + if (node_path.getLastComparisonResult().getRelation() == + NameComparisonResult::COMMONANCESTOR) { + // This means, e.g., we have *.wild.example and + // bar.foo.wild.example and are looking for + // baz.foo.wild.example. The common ancestor, foo.wild.example, + // should cancel wildcard. Treat it as NXDOMAIN. + LOG_DEBUG(logger, DBG_TRACE_DATA, + DATASRC_MEM_WILDCARD_CANCEL).arg(name); + const ZoneNode* nsec_node; + const RdataSet* nsec_rds = getClosestNSEC(zone_data, + node_path, + &nsec_node, + options); + return (FindNodeResult(ZoneFinder::NXDOMAIN, nsec_node, + nsec_rds)); + } + uint8_t ls_buf[LabelSequence::MAX_SERIALIZED_LENGTH]; + + // Create the wildcard name (i.e. take "*" and extend it + // with all node labels down to the wildcard node + LabelSequence wildcard_ls(LabelSequence::WILDCARD(), ls_buf); + const ZoneNode* extend_with = node; + while (extend_with != NULL) { + wildcard_ls.extend(extend_with->getLabels(), ls_buf); + extend_with = extend_with->getUpperNode(); + } + + // Clear the node_path so that we don't keep incorrect (NSEC) + // context + node_path.clear(); + ZoneTree::Result result = tree.find(LabelSequence(wildcard_ls), + &node, node_path, cutCallback, + &state); + // Otherwise, why would the domain_flag::WILD be there if + // there was no wildcard under it? + assert(result == ZoneTree::EXACTMATCH); + return (FindNodeResult(ZoneFinder::SUCCESS, node, state.rrset_, + FindNodeResult::FIND_WILDCARD | zonecut_flag)); + } + LOG_DEBUG(logger, DBG_TRACE_DATA, DATASRC_MEM_NOT_FOUND).arg(name); const ZoneNode* nsec_node; const RdataSet* nsec_rds = getClosestNSEC(zone_data, node_path, @@ -456,7 +512,9 @@ InMemoryZoneFinder::find_internal(const isc::dns::Name& name, assert(node != NULL); // We've found an exact match, may or may not be a result of wildcard. - // TODO, ticket #2110 + const bool wild = ((node_result.flags & + FindNodeResult::FIND_WILDCARD) != 0); + // If there is an exact match but the node is empty, it's equivalent // to NXRRSET. if (node->isEmpty()) { @@ -467,7 +525,8 @@ InMemoryZoneFinder::find_internal(const isc::dns::Name& name, &nsec_node, options); return (createFindResult(rrclass_, zone_data_, NXRRSET, nsec_rds, - nsec_node)); + nsec_node, + wild)); } const RdataSet* found; @@ -482,9 +541,8 @@ InMemoryZoneFinder::find_internal(const isc::dns::Name& name, if (found != NULL) { LOG_DEBUG(logger, DBG_TRACE_DATA, DATASRC_MEM_EXACT_DELEGATION).arg(name); - // TODO: rename argument (wildcards, see #2110) return (createFindResult(rrclass_, zone_data_, DELEGATION, - found, node)); + found, node, wild, &name)); } } @@ -493,12 +551,14 @@ InMemoryZoneFinder::find_internal(const isc::dns::Name& name, // Empty domain will be handled as NXRRSET by normal processing const RdataSet* cur_rds = node->getData(); while (cur_rds != NULL) { - target->push_back(createTreeNodeRRset(node, cur_rds, rrclass_)); + target->push_back(createTreeNodeRRset(node, cur_rds, rrclass_, + &name)); cur_rds = cur_rds->getNext(); } LOG_DEBUG(logger, DBG_TRACE_DATA, DATASRC_MEM_ANY_SUCCESS). arg(name); - return (createFindResult(rrclass_, zone_data_, SUCCESS, NULL, node)); + return (createFindResult(rrclass_, zone_data_, SUCCESS, NULL, node, + wild, &name)); } const RdataSet* currds = node->getData(); @@ -510,19 +570,22 @@ InMemoryZoneFinder::find_internal(const isc::dns::Name& name, // Good, it is here LOG_DEBUG(logger, DBG_TRACE_DATA, DATASRC_MEM_SUCCESS).arg(name). arg(type); - return (createFindResult(rrclass_, zone_data_, SUCCESS, found, node)); + return (createFindResult(rrclass_, zone_data_, SUCCESS, found, node, + wild, &name)); } else { // Next, try CNAME. found = RdataSet::find(node->getData(), RRType::CNAME()); if (found != NULL) { + LOG_DEBUG(logger, DBG_TRACE_DATA, DATASRC_MEM_CNAME).arg(name); - return (createFindResult(rrclass_, zone_data_, CNAME, found, node)); + return (createFindResult(rrclass_, zone_data_, CNAME, found, node, + wild, &name)); } } // No exact match or CNAME. Get NSEC if necessary and return NXRRSET. return (createFindResult(rrclass_, zone_data_, NXRRSET, getNSECForNXRRSET(zone_data_, options, node), - node)); + node, wild, &name)); } isc::datasrc::ZoneFinder::FindNSEC3Result From 56afaa4d876b6af0814e396c95d9375305878737 Mon Sep 17 00:00:00 2001 From: Mukund Sivaraman Date: Fri, 14 Sep 2012 10:47:07 +0530 Subject: [PATCH 165/199] [2108] Changed proto of ZoneTable::setZoneData() to return a FindResult This is so we can differentiate between whether a node was not found and whether a node was found, but its old data was empty. We also don't set the node data now when an exact match was not found. Before it used to set for partial matches too (which was a bug). --- src/lib/datasrc/memory/memory_client.cc | 8 +++++--- src/lib/datasrc/memory/zone_table.cc | 11 +++++------ src/lib/datasrc/memory/zone_table.h | 6 +++--- 3 files changed, 13 insertions(+), 12 deletions(-) diff --git a/src/lib/datasrc/memory/memory_client.cc b/src/lib/datasrc/memory/memory_client.cc index 5617690d29..b818bddb48 100644 --- a/src/lib/datasrc/memory/memory_client.cc +++ b/src/lib/datasrc/memory/memory_client.cc @@ -620,9 +620,11 @@ InMemoryClient::InMemoryClientImpl::load( ++zone_count_; } - ZoneData *data = zone_table_->setZoneData(zone_name, holder.release()); - if (data != NULL) { - ZoneData::destroy(local_mem_sgmt_, data, rrclass_); + ZoneTable::FindResult fr(zone_table_->setZoneData(zone_name, + holder.release())); + assert(fr.code == result::SUCCESS); + if (fr.zone_data != NULL) { + ZoneData::destroy(local_mem_sgmt_, fr.zone_data, rrclass_); } return (result.code); diff --git a/src/lib/datasrc/memory/zone_table.cc b/src/lib/datasrc/memory/zone_table.cc index f2cddc0463..a74a61dd27 100644 --- a/src/lib/datasrc/memory/zone_table.cc +++ b/src/lib/datasrc/memory/zone_table.cc @@ -132,19 +132,18 @@ ZoneTable::findZone(const Name& name) const { return (FindResult(my_result, node->getData())); } -ZoneData* +ZoneTable::FindResult ZoneTable::setZoneData(const Name& name, ZoneData* data) { ZoneTableNode* node(NULL); ZoneTableTree::Result result(zones_->find(name, &node)); - if ((result != ZoneTableTree::EXACTMATCH) && - (result != ZoneTableTree::PARTIALMATCH)) { - return (NULL); + if (result != ZoneTableTree::EXACTMATCH) { + return (FindResult(result::NOTFOUND, NULL)); + } else { + return (FindResult(result::SUCCESS, node->setData(data))); } - - return (node->setData(data)); } } // end of namespace memory diff --git a/src/lib/datasrc/memory/zone_table.h b/src/lib/datasrc/memory/zone_table.h index 733e339e61..8ad6213724 100644 --- a/src/lib/datasrc/memory/zone_table.h +++ b/src/lib/datasrc/memory/zone_table.h @@ -191,9 +191,9 @@ public: /// /// \param name A domain name for which the zone data is set. /// \param data The new zone data to set. - /// \return A \c ZoneData object containing the old data if the zone - /// was found, or \c NULL otherwise. - ZoneData* setZoneData(const isc::dns::Name& name, ZoneData* data); + /// \return A \c FindResult object containing the old data if the + /// zone was found. + FindResult setZoneData(const isc::dns::Name& name, ZoneData* data); private: boost::interprocess::offset_ptr zones_; From 514cf93463cd28391d257672726077717580a9c3 Mon Sep 17 00:00:00 2001 From: Mukund Sivaraman Date: Fri, 14 Sep 2012 10:49:55 +0530 Subject: [PATCH 166/199] [2108] Sort the memory_messages.mes file --- src/lib/datasrc/memory/memory_messages.mes | 94 +++++++++++----------- 1 file changed, 47 insertions(+), 47 deletions(-) diff --git a/src/lib/datasrc/memory/memory_messages.mes b/src/lib/datasrc/memory/memory_messages.mes index a66297c334..1b67093099 100644 --- a/src/lib/datasrc/memory/memory_messages.mes +++ b/src/lib/datasrc/memory/memory_messages.mes @@ -16,46 +16,6 @@ $NAMESPACE isc::datasrc::memory # \brief Messages for the data source memory library -% DATASRC_MEMORY_MEM_ADD_WILDCARD adding wildcards for '%1' -This is a debug message issued during the processing of a wildcard -name. The internal domain name tree is scanned and some nodes are -specially marked to allow the wildcard lookup to succeed. - -% DATASRC_MEMORY_MEM_CNAME_TO_NONEMPTY can't add CNAME to domain with other data in '%1' -Someone or something tried to add a CNAME into a domain that already contains -some other data. But the protocol forbids coexistence of CNAME with anything -(RFC 1034, section 3.6.2). This indicates a problem with provided data. - -% DATASRC_MEMORY_MEM_CNAME_COEXIST can't add data to CNAME in domain '%1' -This is the same problem as in MEM_CNAME_TO_NONEMPTY, but it happened the -other way around -- adding some other data to CNAME. - -% DATASRC_MEMORY_MEM_DNAME_NS DNAME and NS can't coexist in non-apex domain '%1' -A request was made for DNAME and NS records to be put into the same -domain which is not the apex (the top of the zone). This is forbidden -by RFC 2672 (section 3) and indicates a problem with provided data. - -% DATASRC_MEMORY_MEM_SINGLETON trying to add multiple RRs for domain '%1' and type '%2' -Some resource types are singletons -- only one is allowed in a domain -(for example CNAME or SOA). This indicates a problem with provided data. - -% DATASRC_MEMORY_MEM_OUT_OF_ZONE domain '%1' doesn't belong to zone '%2' -It was attempted to add the domain into a zone that shouldn't have it -(eg. the domain is not subdomain of the zone origin). This indicates a -problem with provided data. - -% DATASRC_MEMORY_MEM_WILDCARD_NS NS record in wildcard domain '%1' -The software refuses to load NS records into a wildcard domain. It isn't -explicitly forbidden, but the protocol is ambiguous about how this should -behave and BIND 9 refuses that as well. Please describe your intention using -different tools. - -% DATASRC_MEMORY_MEM_WILDCARD_DNAME DNAME record in wildcard domain '%1' -The software refuses to load DNAME records into a wildcard domain. It isn't -explicitly forbidden, but the protocol is ambiguous about how this should -behave and BIND 9 refuses that as well. Please describe your intention using -different tools. - % DATASRC_MEMORY_BAD_NSEC3_NAME NSEC3 record has a bad owner name '%1' The software refuses to load NSEC3 records into a wildcard domain or the owner name has two or more labels below the zone origin. @@ -66,11 +26,40 @@ compatible with BIND 9. % DATASRC_MEMORY_MEM_ADD_RRSET adding RRset '%1/%2' into zone '%3' Debug information. An RRset is being added to the in-memory data source. +% DATASRC_MEMORY_MEM_ADD_WILDCARD adding wildcards for '%1' +This is a debug message issued during the processing of a wildcard +name. The internal domain name tree is scanned and some nodes are +specially marked to allow the wildcard lookup to succeed. + +% DATASRC_MEMORY_MEM_ADD_ZONE adding zone '%1/%2' +Debug information. A zone is being added into the in-memory data source. + +% DATASRC_MEMORY_MEM_CNAME_COEXIST can't add data to CNAME in domain '%1' +This is the same problem as in MEM_CNAME_TO_NONEMPTY, but it happened the +other way around -- adding some other data to CNAME. + +% DATASRC_MEMORY_MEM_CNAME_TO_NONEMPTY can't add CNAME to domain with other data in '%1' +Someone or something tried to add a CNAME into a domain that already contains +some other data. But the protocol forbids coexistence of CNAME with anything +(RFC 1034, section 3.6.2). This indicates a problem with provided data. + +% DATASRC_MEMORY_MEM_DNAME_NS DNAME and NS can't coexist in non-apex domain '%1' +A request was made for DNAME and NS records to be put into the same +domain which is not the apex (the top of the zone). This is forbidden +by RFC 2672 (section 3) and indicates a problem with provided data. + % DATASRC_MEMORY_MEM_DUP_RRSET duplicate RRset '%1/%2' An RRset is being inserted into in-memory data source for a second time. The original version must be removed first. Note that loading master files where an RRset is split into multiple locations is not supported yet. +% DATASRC_MEMORY_MEM_FIND_ZONE looking for zone '%1' +Debug information. A zone object for this zone is being searched for in the +in-memory data source. + +% DATASRC_MEMORY_MEM_LOAD loading zone '%1' from file '%2' +Debug information. The content of master file is being loaded into the memory. + % DATASRC_MEMORY_MEM_NO_NSEC3PARAM NSEC3PARAM is missing for NSEC3-signed zone %1/%2 The in-memory data source has loaded a zone signed with NSEC3 RRs, but it doesn't have a NSEC3PARAM RR at the zone origin. It's likely that @@ -79,12 +68,23 @@ handling lookups with NSEC3 in this data source, so it accepts the given content of the zone. Nevertheless the administrator should look into the integrity of the zone data. -% DATASRC_MEMORY_MEM_ADD_ZONE adding zone '%1/%2' -Debug information. A zone is being added into the in-memory data source. +% DATASRC_MEMORY_MEM_OUT_OF_ZONE domain '%1' doesn't belong to zone '%2' +It was attempted to add the domain into a zone that shouldn't have it +(eg. the domain is not subdomain of the zone origin). This indicates a +problem with provided data. -% DATASRC_MEMORY_MEM_FIND_ZONE looking for zone '%1' -Debug information. A zone object for this zone is being searched for in the -in-memory data source. +% DATASRC_MEMORY_MEM_SINGLETON trying to add multiple RRs for domain '%1' and type '%2' +Some resource types are singletons -- only one is allowed in a domain +(for example CNAME or SOA). This indicates a problem with provided data. -% DATASRC_MEMORY_MEM_LOAD loading zone '%1' from file '%2' -Debug information. The content of master file is being loaded into the memory. +% DATASRC_MEMORY_MEM_WILDCARD_DNAME DNAME record in wildcard domain '%1' +The software refuses to load DNAME records into a wildcard domain. It isn't +explicitly forbidden, but the protocol is ambiguous about how this should +behave and BIND 9 refuses that as well. Please describe your intention using +different tools. + +% DATASRC_MEMORY_MEM_WILDCARD_NS NS record in wildcard domain '%1' +The software refuses to load NS records into a wildcard domain. It isn't +explicitly forbidden, but the protocol is ambiguous about how this should +behave and BIND 9 refuses that as well. Please describe your intention using +different tools. From 2de58c844cbf6ab10fa9bbe2e45b09badcd3101b Mon Sep 17 00:00:00 2001 From: Mukund Sivaraman Date: Fri, 14 Sep 2012 10:51:27 +0530 Subject: [PATCH 167/199] [2108] Update obsolete comment --- src/lib/datasrc/memory/memory_client.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/lib/datasrc/memory/memory_client.h b/src/lib/datasrc/memory/memory_client.h index a28125950f..bff55f4665 100644 --- a/src/lib/datasrc/memory/memory_client.h +++ b/src/lib/datasrc/memory/memory_client.h @@ -45,8 +45,8 @@ namespace memory { /// allowing much faster lookups. The in memory data is a copy of some /// real physical source - in the current implementation a list of zones /// are populated as a result of \c load() calls; zone data is given in -/// a standard master file (but there's a plan to use database backends -/// as a source of the in memory data). +/// a standard master file, or as an iterator of some other datasource +/// including database backed ones. /// /// The InMemoryClient enforces through its interface that all data /// loaded to the data source is of the same RR class. For example, the From 7f4c06a3fcbbff054ff5227b7b3b60f865b8ed46 Mon Sep 17 00:00:00 2001 From: Mukund Sivaraman Date: Fri, 14 Sep 2012 10:55:38 +0530 Subject: [PATCH 168/199] [2108] Fix doc comment for InMemoryClient::getFileName() --- src/lib/datasrc/memory/memory_client.h | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/lib/datasrc/memory/memory_client.h b/src/lib/datasrc/memory/memory_client.h index bff55f4665..dd0058efb2 100644 --- a/src/lib/datasrc/memory/memory_client.h +++ b/src/lib/datasrc/memory/memory_client.h @@ -132,14 +132,15 @@ public: /// /// This method returns the name of the zone's master file to be loaded. /// The returned string will be an empty unless the data source client has - /// successfully loaded the zone before. + /// successfully loaded the \c zone_name zone from a file before. /// /// This method should normally not throw an exception. But the creation /// of the return string may involve a resource allocation, and if it /// fails, the corresponding standard exception will be thrown. /// - /// \return The name of the zone file loaded in the client, or an empty - /// string if the client hasn't loaded any file. + /// \return The name of the zone file corresponding to the zone, or + /// an empty string if the client hasn't loaded the \c zone_name + /// zone from a file before. const std::string getFileName(const isc::dns::Name& zone_name) const; /// \brief Inserts an rrset into the zone. From 65ce434abe243852100803da6f0ba086f55fda5b Mon Sep 17 00:00:00 2001 From: Mukund Sivaraman Date: Fri, 14 Sep 2012 10:56:46 +0530 Subject: [PATCH 169/199] [2108] Fix typo --- src/lib/datasrc/memory/memory_client.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib/datasrc/memory/memory_client.h b/src/lib/datasrc/memory/memory_client.h index dd0058efb2..d2ec4c3c56 100644 --- a/src/lib/datasrc/memory/memory_client.h +++ b/src/lib/datasrc/memory/memory_client.h @@ -223,7 +223,7 @@ public: virtual isc::datasrc::ZoneIteratorPtr getIterator(const isc::dns::Name& name, bool separate_rrs = false) const; - /// In-memory data source doesn't write back peristently, so this + /// In-memory data source doesn't write back persistently, so this /// derived method will result in a NotImplemented exception. /// /// \note We plan to use a database-based data source as a backend From 6919cf078b8864752a438346b662ace8d4acaa59 Mon Sep 17 00:00:00 2001 From: Mukund Sivaraman Date: Fri, 14 Sep 2012 11:03:00 +0530 Subject: [PATCH 170/199] [2108] Fix doc comment for addFromLoad() addFromLoad() used to throw before, but now the called code itself throws. add() should never return anything except SUCCESS, so if it returns anything else, we assert. --- src/lib/datasrc/memory/memory_client.cc | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/lib/datasrc/memory/memory_client.cc b/src/lib/datasrc/memory/memory_client.cc index b818bddb48..1674095979 100644 --- a/src/lib/datasrc/memory/memory_client.cc +++ b/src/lib/datasrc/memory/memory_client.cc @@ -529,8 +529,7 @@ public: } /* - * Same as above, but it checks the return value and if it already exists, - * it throws. + * Wrapper around above. */ void addFromLoad(const ConstRRsetPtr& set, const Name& zone_name, ZoneData* zone_data) From 70f25f4ac5d30d17d87c8be3b138d29c081eaee4 Mon Sep 17 00:00:00 2001 From: Mukund Sivaraman Date: Fri, 14 Sep 2012 11:20:05 +0530 Subject: [PATCH 171/199] [2108] Remove unused variable (reported by cppcheck) --- src/lib/datasrc/memory/tests/memory_client_unittest.cc | 1 - 1 file changed, 1 deletion(-) diff --git a/src/lib/datasrc/memory/tests/memory_client_unittest.cc b/src/lib/datasrc/memory/tests/memory_client_unittest.cc index 86dd674a5a..6952a7175d 100644 --- a/src/lib/datasrc/memory/tests/memory_client_unittest.cc +++ b/src/lib/datasrc/memory/tests/memory_client_unittest.cc @@ -122,7 +122,6 @@ protected: const RRClass zclass_; TestMemorySegment mem_sgmt_; InMemoryClient* client_; - memory::ZoneTable* zone_table; }; TEST_F(MemoryClientTest, loadRRsetDoesntMatchOrigin) { From 8ad1accced4284ff727b03c43cab755566e486ab Mon Sep 17 00:00:00 2001 From: Marcin Siodelski Date: Fri, 14 Sep 2012 10:10:15 +0200 Subject: [PATCH 172/199] [master] Use CommandOptionsHelper for CommandOptions unit tests. This fixes memory leak reported by Valgrind. Dicussed with Jelte on jabber. --- .../tests/command_options_unittest.cc | 49 +------------------ 1 file changed, 2 insertions(+), 47 deletions(-) diff --git a/tests/tools/perfdhcp/tests/command_options_unittest.cc b/tests/tools/perfdhcp/tests/command_options_unittest.cc index 0481d28da3..801b02dd29 100644 --- a/tests/tools/perfdhcp/tests/command_options_unittest.cc +++ b/tests/tools/perfdhcp/tests/command_options_unittest.cc @@ -21,7 +21,7 @@ #include #include -#include "../command_options.h" +#include "command_options_helper.h" using namespace std; using namespace isc; @@ -48,16 +48,7 @@ protected: /// \param cmdline Command line to parse /// \throws std::bad allocation if tokenization failed void process(const std::string& cmdline) { - CommandOptions& opt = CommandOptions::instance(); - int argc = 0; - char** argv = tokenizeString(cmdline, &argc); - opt.reset(); - opt.parse(argc, argv); - for(int i = 0; i < argc; ++i) { - free(argv[i]); - argv[i] = NULL; - } - free(argv); + CommandOptionsHelper::process(cmdline); } /// \brief Check default initialized values @@ -143,42 +134,6 @@ protected: EXPECT_EQ("", opt.getWrapped()); EXPECT_EQ("192.168.0.1", opt.getServerName()); } - - /// \brief Split string to array of C-strings - /// - /// \param s String to split (tokenize) - /// \param num Number of tokens returned - /// \return array of C-strings (tokens) - char** tokenizeString(const std::string& text_to_split, int* num) const { - char** results = NULL; - // Tokenization with std streams - std::stringstream text_stream(text_to_split); - // Iterators to be used for tokenization - std::istream_iterator text_iterator(text_stream); - std::istream_iterator text_end; - // Tokenize string (space is a separator) using begin and end iteratos - std::vector tokens(text_iterator, text_end); - - if (tokens.size() > 0) { - // Allocate array of C-strings where we will store tokens - results = static_cast(malloc(tokens.size() * sizeof(char*))); - if (results == NULL) { - throw std::bad_alloc(); - } - // Store tokens in C-strings array - for (int i = 0; i < tokens.size(); ++i) { - char* cs = static_cast(malloc(tokens[i].length() + 1)); - strcpy(cs, tokens[i].c_str()); - results[i] = cs; - } - // Return number of tokens to calling function - if (num != NULL) { - *num = tokens.size(); - } - } - return results; - } - }; TEST_F(CommandOptionsTest, Defaults) { From d6f3e1aa02ffc1de9d55a586174022eb60f43887 Mon Sep 17 00:00:00 2001 From: Stephen Morris Date: Fri, 14 Sep 2012 15:16:56 +0100 Subject: [PATCH 173/199] [1545] Corrected problems in setting environment variable in tests For some reason, the Python "os.putenv()" did not work, so changed to morifying the "os.environ" variable directly. --- src/bin/dhcp4/tests/dhcp4_test.py | 8 ++++---- src/bin/dhcp6/tests/dhcp6_test.py | 8 ++++---- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/bin/dhcp4/tests/dhcp4_test.py b/src/bin/dhcp4/tests/dhcp4_test.py index 3ad97c6c98..efe16fb7a9 100644 --- a/src/bin/dhcp4/tests/dhcp4_test.py +++ b/src/bin/dhcp4/tests/dhcp4_test.py @@ -32,11 +32,11 @@ class TestDhcpv4Daemon(unittest.TestCase): # # However, we do want to set the logging lock directory to somewhere # to which we can write - use the current working directory. We then - # set the appropriate environment variable. + # set the appropriate environment variable. os.putenv() doesn't work + # on Ubuntu, so we access os.environ directly. lockdir_envvar = "B10_LOCKFILE_DIR_FROM_BUILD" - lockdir = os.getenv(lockdir_envvar) - if lockdir is None: - os.putenv(lockdir_envvar, os.getcwd()) + if lockdir_envvar not in os.environ: + os.environ[lockdir_envvar] = os.getcwd() def tearDown(self): pass diff --git a/src/bin/dhcp6/tests/dhcp6_test.py b/src/bin/dhcp6/tests/dhcp6_test.py index 0887bbf2a0..78202bd910 100644 --- a/src/bin/dhcp6/tests/dhcp6_test.py +++ b/src/bin/dhcp6/tests/dhcp6_test.py @@ -32,11 +32,11 @@ class TestDhcpv6Daemon(unittest.TestCase): # # However, we do want to set the logging lock directory to somewhere # to which we can write - use the current working directory. We then - # set the appropriate environment variable. + # set the appropriate environment variable. os.putenv() doesn't work + # on Ubuntu, so we access os.environ directly. lockdir_envvar = "B10_LOCKFILE_DIR_FROM_BUILD" - lockdir = os.getenv(lockdir_envvar) - if lockdir is None: - os.putenv(lockdir_envvar, os.getcwd()) + if lockdir_envvar not in os.environ: + os.environ[lockdir_envvar] = os.getcwd() def tearDown(self): pass From 38c6a4866e5be6f6a7ed3497f0fef252583b5be4 Mon Sep 17 00:00:00 2001 From: Marcin Siodelski Date: Fri, 14 Sep 2012 19:14:34 +0200 Subject: [PATCH 174/199] [1545] Added reference to python documentation for os.putenv() function. The os.putenv() functionsseems to be not supported on all platforms and this is indicated in the python documentation. --- src/bin/dhcp4/tests/dhcp4_test.py | 8 ++++++-- src/bin/dhcp6/tests/dhcp6_test.py | 8 ++++++-- 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/src/bin/dhcp4/tests/dhcp4_test.py b/src/bin/dhcp4/tests/dhcp4_test.py index efe16fb7a9..444dfcfd2a 100644 --- a/src/bin/dhcp4/tests/dhcp4_test.py +++ b/src/bin/dhcp4/tests/dhcp4_test.py @@ -32,8 +32,12 @@ class TestDhcpv4Daemon(unittest.TestCase): # # However, we do want to set the logging lock directory to somewhere # to which we can write - use the current working directory. We then - # set the appropriate environment variable. os.putenv() doesn't work - # on Ubuntu, so we access os.environ directly. + # set the appropriate environment variable. os.putenv() may be not + # supported on some platforms as suggested in + # http://docs.python.org/release/3.2/library/os.html?highlight=putenv#os.environ: + # "If the platform supports the putenv() function...". It was checked + # that it does not work on Ubuntu. To overcome this problem we access + # os.environ directly. lockdir_envvar = "B10_LOCKFILE_DIR_FROM_BUILD" if lockdir_envvar not in os.environ: os.environ[lockdir_envvar] = os.getcwd() diff --git a/src/bin/dhcp6/tests/dhcp6_test.py b/src/bin/dhcp6/tests/dhcp6_test.py index 78202bd910..f3099b9a83 100644 --- a/src/bin/dhcp6/tests/dhcp6_test.py +++ b/src/bin/dhcp6/tests/dhcp6_test.py @@ -32,8 +32,12 @@ class TestDhcpv6Daemon(unittest.TestCase): # # However, we do want to set the logging lock directory to somewhere # to which we can write - use the current working directory. We then - # set the appropriate environment variable. os.putenv() doesn't work - # on Ubuntu, so we access os.environ directly. + # set the appropriate environment variable. os.putenv() may be not + # supported on some platforms as suggested in + # http://docs.python.org/release/3.2/library/os.html?highlight=putenv#os.environ: + # "If the platform supports the putenv() function...". It was checked + # that it does not work on Ubuntu. To overcome this problem we access + # os.environ directly. lockdir_envvar = "B10_LOCKFILE_DIR_FROM_BUILD" if lockdir_envvar not in os.environ: os.environ[lockdir_envvar] = os.getcwd() From 0b66c1db1a3016532fca5e8ff4be11299646aaf7 Mon Sep 17 00:00:00 2001 From: Mukund Sivaraman Date: Mon, 17 Sep 2012 01:02:36 +0530 Subject: [PATCH 175/199] [2150] Check that non-absolute label sequence + empty chain throws in find() --- .../datasrc/memory/tests/domaintree_unittest.cc | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/src/lib/datasrc/memory/tests/domaintree_unittest.cc b/src/lib/datasrc/memory/tests/domaintree_unittest.cc index 9229aed2dc..4c23cc4785 100644 --- a/src/lib/datasrc/memory/tests/domaintree_unittest.cc +++ b/src/lib/datasrc/memory/tests/domaintree_unittest.cc @@ -459,14 +459,22 @@ TEST_F(DomainTreeTest, findInSubTree) { DomainTreeNodeChain chain; bool flag; + // Searching for a non-absolute (right-stripped) label sequence when + // chain is empty should throw. + const Name n0("w.y.d.e.f"); + LabelSequence ls0(n0); + ls0.stripRight(1); + EXPECT_THROW(dtree_expose_empty_node.find(ls0, &cdtnode, chain, + testCallback, &flag), + isc::BadValue); + // First, find a sub-tree node - const Name n1("w.y.d.e.f"); - const LabelSequence ls1(n1); + const LabelSequence ls1(n0); DomainTree::Result result = dtree_expose_empty_node.find(ls1, &cdtnode, chain, testCallback, &flag); EXPECT_EQ(DomainTree::EXACTMATCH, result); - EXPECT_EQ(n1, chain.getAbsoluteName()); + EXPECT_EQ(n0, chain.getAbsoluteName()); // Searching for an absolute label sequence when chain is already // populated should throw. From 0ccc72ed6a2a8298af79e19c4b13935471652947 Mon Sep 17 00:00:00 2001 From: Mukund Sivaraman Date: Mon, 17 Sep 2012 01:11:22 +0530 Subject: [PATCH 176/199] [2150] Clear chain before re-use --- src/lib/datasrc/memory/tests/domaintree_unittest.cc | 1 + 1 file changed, 1 insertion(+) diff --git a/src/lib/datasrc/memory/tests/domaintree_unittest.cc b/src/lib/datasrc/memory/tests/domaintree_unittest.cc index 4c23cc4785..939836e929 100644 --- a/src/lib/datasrc/memory/tests/domaintree_unittest.cc +++ b/src/lib/datasrc/memory/tests/domaintree_unittest.cc @@ -469,6 +469,7 @@ TEST_F(DomainTreeTest, findInSubTree) { isc::BadValue); // First, find a sub-tree node + chain.clear(); const LabelSequence ls1(n0); DomainTree::Result result = dtree_expose_empty_node.find(ls1, &cdtnode, chain, From 65ef86855e3fc12b765ba3af403e92af112578da Mon Sep 17 00:00:00 2001 From: Mukund Sivaraman Date: Mon, 17 Sep 2012 01:12:04 +0530 Subject: [PATCH 177/199] [2150] Check label sequence to see that it's correct --- src/lib/datasrc/memory/tests/domaintree_unittest.cc | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/lib/datasrc/memory/tests/domaintree_unittest.cc b/src/lib/datasrc/memory/tests/domaintree_unittest.cc index 939836e929..fa3665e87b 100644 --- a/src/lib/datasrc/memory/tests/domaintree_unittest.cc +++ b/src/lib/datasrc/memory/tests/domaintree_unittest.cc @@ -486,10 +486,11 @@ TEST_F(DomainTreeTest, findInSubTree) { isc::BadValue); // Now, find "o.w.y.d.e.f." by right-stripping the "w.y.d.e.f." - // suffix. + // suffix to "o" (non-absolute). const Name n2("o.w.y.d.e.f"); LabelSequence ls2(n2); ls2.stripRight(6); + EXPECT_EQ("o", ls2.toText()); result = dtree_expose_empty_node.find(ls2, &cdtnode, chain, testCallback, &flag); From 4e25ff76b698c04d2aa9f0e2ed391a3b6922b8f7 Mon Sep 17 00:00:00 2001 From: Mukund Sivaraman Date: Mon, 17 Sep 2012 01:12:32 +0530 Subject: [PATCH 178/199] [2150] Find a relative label sequence that's more than 1 node away from chain.top() --- .../memory/tests/domaintree_unittest.cc | 22 +++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/src/lib/datasrc/memory/tests/domaintree_unittest.cc b/src/lib/datasrc/memory/tests/domaintree_unittest.cc index fa3665e87b..58a3f4d4df 100644 --- a/src/lib/datasrc/memory/tests/domaintree_unittest.cc +++ b/src/lib/datasrc/memory/tests/domaintree_unittest.cc @@ -496,6 +496,28 @@ TEST_F(DomainTreeTest, findInSubTree) { testCallback, &flag); EXPECT_EQ(DomainTree::EXACTMATCH, result); EXPECT_EQ(n2, chain.getAbsoluteName()); + + // Another test. Start with "d.e.f." node. + chain.clear(); + const Name n3("d.e.f"); + const LabelSequence ls3(n3); + result = + dtree_expose_empty_node.find(ls3, &cdtnode, chain, + testCallback, &flag); + EXPECT_EQ(DomainTree::EXACTMATCH, result); + EXPECT_EQ(n3, chain.getAbsoluteName()); + + // Now, find "o.w.y.d.e.f." by right-stripping the "w.y.d.e.f." + // suffix to "o.w.y" (non-absolute). + const Name n4("o.w.y.d.e.f"); + LabelSequence ls4(n2); + ls4.stripRight(4); + EXPECT_EQ("o.w.y", ls4.toText()); + + result = dtree_expose_empty_node.find(ls4, &cdtnode, chain, + testCallback, &flag); + EXPECT_EQ(DomainTree::EXACTMATCH, result); + EXPECT_EQ(n4, chain.getAbsoluteName()); } TEST_F(DomainTreeTest, chainLevel) { From dc8237b41256dcdb50220a9937aa6426f96b981a Mon Sep 17 00:00:00 2001 From: Mukund Sivaraman Date: Mon, 17 Sep 2012 01:26:20 +0530 Subject: [PATCH 179/199] [2150] Add test to find the same non-absolute label sequence at two different places --- .../memory/tests/domaintree_unittest.cc | 50 +++++++++++++++++++ 1 file changed, 50 insertions(+) diff --git a/src/lib/datasrc/memory/tests/domaintree_unittest.cc b/src/lib/datasrc/memory/tests/domaintree_unittest.cc index 58a3f4d4df..69ead4308e 100644 --- a/src/lib/datasrc/memory/tests/domaintree_unittest.cc +++ b/src/lib/datasrc/memory/tests/domaintree_unittest.cc @@ -520,6 +520,56 @@ TEST_F(DomainTreeTest, findInSubTree) { EXPECT_EQ(n4, chain.getAbsoluteName()); } +TEST_F(DomainTreeTest, findInSubTreeSameLabelSequence) { + // For the version that takes a node chain, the chain must be empty. + DomainTreeNodeChain chain; + bool flag; + + const Name n1("c.g.h"); + + // First insert a "c.g.h." node. + dtree_expose_empty_node.insert(mem_sgmt_, n1, &dtnode); + + // Make a non-absolute label sequence. We will search for this same + // sequence in two places in the tree. + LabelSequence ls1(n1); + ls1.stripRight(3); + EXPECT_EQ("c", ls1.toText()); + + // First, find "g.h." + const Name n2("g.h"); + const LabelSequence ls2(n2); + DomainTree::Result result = + dtree_expose_empty_node.find(ls2, &cdtnode, chain, + testCallback, &flag); + EXPECT_EQ(DomainTree::EXACTMATCH, result); + EXPECT_EQ(n2, chain.getAbsoluteName()); + + // Now, find "c.g.h." by searching just the non-absolute ls1 label + // sequence. + result = dtree_expose_empty_node.find(ls1, &cdtnode, chain, + testCallback, &flag); + EXPECT_EQ(DomainTree::EXACTMATCH, result); + EXPECT_EQ(n1, chain.getAbsoluteName()); + + // Now, find "." (the root node) + chain.clear(); + const Name n3("."); + const LabelSequence ls3(n3); + result = + dtree_expose_empty_node.find(ls3, &cdtnode, chain, + testCallback, &flag); + EXPECT_EQ(DomainTree::EXACTMATCH, result); + EXPECT_EQ(n3, chain.getAbsoluteName()); + + // Now, find "c." by searching just the non-absolute ls1 label + // sequence. + result = dtree_expose_empty_node.find(ls1, &cdtnode, chain, + testCallback, &flag); + EXPECT_EQ(DomainTree::EXACTMATCH, result); + EXPECT_EQ(Name("c."), chain.getAbsoluteName()); +} + TEST_F(DomainTreeTest, chainLevel) { TestDomainTreeNodeChain chain; From 5b8f36f524d99d1e97d0c562153ef6102c10bb93 Mon Sep 17 00:00:00 2001 From: Mukund Sivaraman Date: Mon, 17 Sep 2012 10:53:48 +0530 Subject: [PATCH 180/199] [2150] Add a diagram of the DomainTree after the node has been added --- .../memory/tests/domaintree_unittest.cc | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/src/lib/datasrc/memory/tests/domaintree_unittest.cc b/src/lib/datasrc/memory/tests/domaintree_unittest.cc index 69ead4308e..ef900509a5 100644 --- a/src/lib/datasrc/memory/tests/domaintree_unittest.cc +++ b/src/lib/datasrc/memory/tests/domaintree_unittest.cc @@ -530,6 +530,26 @@ TEST_F(DomainTreeTest, findInSubTreeSameLabelSequence) { // First insert a "c.g.h." node. dtree_expose_empty_node.insert(mem_sgmt_, n1, &dtnode); + /* Now, the tree looks like: + * + * . + * | + * b + * / \ + * a d.e.f + * / | \____ + * c | \ + * | g.h + * | | + * w.y i + * / | \ / \ + * x | z c k + * | | + * p j + * / \ + * o q + */ + // Make a non-absolute label sequence. We will search for this same // sequence in two places in the tree. LabelSequence ls1(n1); From 68ebb92883a7cd9bb84b3b39cab2049b59119e36 Mon Sep 17 00:00:00 2001 From: Stephen Morris Date: Mon, 17 Sep 2012 11:54:07 +0100 Subject: [PATCH 181/199] [master] ChangeLog entry for ticket 1545. --- ChangeLog | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/ChangeLog b/ChangeLog index da4a82666a..dfcfa606f5 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,7 @@ +474. [func] stephen + DHCP servers now use the BIND 10 logging system for messages. + (Trac #1545, git de69a92613b36bd3944cb061e1b7c611c3c85506) + 473. [bug] jelte TCP connections now time out in b10-auth if no (or not all) query data is sent by the client. The timeout value defaults to 5000 From f79bca027235227542cc763f4fbbc45aec365553 Mon Sep 17 00:00:00 2001 From: Stephen Morris Date: Mon, 17 Sep 2012 15:03:12 +0100 Subject: [PATCH 182/199] [master] Changes to fix "make distcheck" failure --- src/bin/dhcp4/tests/Makefile.am | 4 ++-- src/bin/dhcp6/tests/Makefile.am | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/bin/dhcp4/tests/Makefile.am b/src/bin/dhcp4/tests/Makefile.am index 0e96d70017..906b5d1e59 100644 --- a/src/bin/dhcp4/tests/Makefile.am +++ b/src/bin/dhcp4/tests/Makefile.am @@ -30,7 +30,7 @@ AM_CPPFLAGS += -DTEST_DATA_DIR=\"$(abs_top_srcdir)/src/lib/testutils/testdata\" AM_CPPFLAGS += -DTEST_DATA_BUILDDIR=\"$(abs_top_builddir)/src/bin/dhcp6/tests\" AM_CPPFLAGS += -DINSTALL_PROG=\"$(abs_top_srcdir)/install-sh\" -CLEANFILES = $(builddir)/interfaces.txt +CLEANFILES = $(builddir)/interfaces.txt $(builddir)/logger_lockfile AM_CXXFLAGS = $(B10_CXXFLAGS) @@ -48,10 +48,10 @@ TESTS += dhcp4_unittests dhcp4_unittests_SOURCES = ../dhcp4_srv.h ../dhcp4_srv.cc ../ctrl_dhcp4_srv.cc dhcp4_unittests_SOURCES += ../dhcp4_log.h ../dhcp4_log.cc -dhcp4_unittests_SOURCES += ../dhcp4_messages.h ../dhcp4_messages.cc dhcp4_unittests_SOURCES += dhcp4_unittests.cc dhcp4_unittests_SOURCES += dhcp4_srv_unittest.cc dhcp4_unittests_SOURCES += ctrl_dhcp4_srv_unittest.cc +nodist_dhcp4_unittests_SOURCES = ../dhcp4_messages.h ../dhcp4_messages.cc if USE_CLANGPP # Disable unused parameter warning caused by some of the diff --git a/src/bin/dhcp6/tests/Makefile.am b/src/bin/dhcp6/tests/Makefile.am index 97d2d08572..1d9308fcaa 100644 --- a/src/bin/dhcp6/tests/Makefile.am +++ b/src/bin/dhcp6/tests/Makefile.am @@ -26,7 +26,7 @@ AM_CPPFLAGS += -I$(top_srcdir)/src/bin AM_CPPFLAGS += $(BOOST_INCLUDES) AM_CPPFLAGS += -DINSTALL_PROG=\"$(abs_top_srcdir)/install-sh\" -CLEANFILES = $(builddir)/interfaces.txt +CLEANFILES = $(builddir)/interfaces.txt $(builddir)/logger_lockfile AM_CXXFLAGS = $(B10_CXXFLAGS) @@ -47,8 +47,8 @@ dhcp6_unittests_SOURCES += dhcp6_srv_unittest.cc dhcp6_unittests_SOURCES += ctrl_dhcp6_srv_unittest.cc dhcp6_unittests_SOURCES += ../dhcp6_srv.h ../dhcp6_srv.cc dhcp6_unittests_SOURCES += ../dhcp6_log.h ../dhcp6_log.cc -dhcp6_unittests_SOURCES += ../dhcp6_messages.h ../dhcp6_messages.cc dhcp6_unittests_SOURCES += ../ctrl_dhcp6_srv.cc +nodist_dhcp6_unittests_SOURCES = ../dhcp6_messages.h ../dhcp6_messages.cc if USE_CLANGPP # Disable unused parameter warning caused by some of the From 750f6fecbe3e5e8b74a309679b129e35895fc841 Mon Sep 17 00:00:00 2001 From: Marcin Siodelski Date: Mon, 17 Sep 2012 16:46:04 +0200 Subject: [PATCH 183/199] [2230] Comments on byte order and endianess in template files. --- tests/tools/dhcp-ubench/dhcp-perf-guide.xml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/tests/tools/dhcp-ubench/dhcp-perf-guide.xml b/tests/tools/dhcp-ubench/dhcp-perf-guide.xml index cbf1bf65a4..ac340b7918 100644 --- a/tests/tools/dhcp-ubench/dhcp-perf-guide.xml +++ b/tests/tools/dhcp-ubench/dhcp-perf-guide.xml @@ -1024,7 +1024,10 @@ collected packets: 0 dynamically (without the template). Note that each template file holds data for exactly one DHCP message type. Templates for multiple message types must not be combined in the single file. - The content in template files is encoded in hexadecimal format. + The content in template files is encoded as series of ASCII hexadecimal + digits (each byte represented by two ASCII chars 00..FF). Data in a + template file is laid in network byte order and it can be used on the + systems with different endianess. perfdhcp forms the packet by replacing parts of the message buffer read from the file with variable data such as elapsed time, hardware address, DUID etc. The offsets where such variable data is placed is specific to the From 8a6f971934a55f86df87c8dc7996d50b2ace0111 Mon Sep 17 00:00:00 2001 From: Marcin Siodelski Date: Mon, 17 Sep 2012 19:38:35 +0200 Subject: [PATCH 184/199] [master] Adding timeout tolerance to receiveX() timeout checks. --- src/lib/dhcp/tests/iface_mgr_unittest.cc | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/src/lib/dhcp/tests/iface_mgr_unittest.cc b/src/lib/dhcp/tests/iface_mgr_unittest.cc index 8db8706bec..3bf5ca4450 100644 --- a/src/lib/dhcp/tests/iface_mgr_unittest.cc +++ b/src/lib/dhcp/tests/iface_mgr_unittest.cc @@ -42,6 +42,13 @@ char LOOPBACK[BUF_SIZE] = "lo"; const uint16_t PORT1 = 10547; // V6 socket const uint16_t PORT2 = 10548; // V4 socket +// On some systems measured duration of receive6() and +// receive4() appears to be shorter than select() timeout. +// called by these functions. This may be the case +// if different ime resolutions are used by these functions. +// For such cases we set the tolerance of 0.01s. +const uint32_t TIMEOUT_TOLERANCE = 10000; + class NakedIfaceMgr: public IfaceMgr { // "naked" Interface Manager, exposes internal fields @@ -250,7 +257,8 @@ TEST_F(IfaceMgrTest, receiveTimeout6) { // precisely reflect the receive timeout. However the // uncertainity should be low enough to expect that measured // value is in the range <1.4s; 1.7s>. - EXPECT_GE(duration.total_microseconds(), 1400000); + EXPECT_GE(duration.total_microseconds(), + 1400000 - TIMEOUT_TOLERANCE); EXPECT_LE(duration.total_microseconds(), 1700000); // Test timeout shorter than 1s. @@ -260,7 +268,8 @@ TEST_F(IfaceMgrTest, receiveTimeout6) { ASSERT_FALSE(pkt); duration = stop_time - start_time; // Check if measured duration is within <0.5s; 0.8s>. - EXPECT_GE(duration.total_microseconds(), 500000); + EXPECT_GE(duration.total_microseconds(), + 500000 - TIMEOUT_TOLERANCE); EXPECT_LE(duration.total_microseconds(), 800000); // Test with invalid fractional timeout values. @@ -300,7 +309,8 @@ TEST_F(IfaceMgrTest, receiveTimeout4) { // precisely reflect the receive timeout. However the // uncertainity should be low enough to expect that measured // value is in the range <2.3s; 2.6s>. - EXPECT_GE(duration.total_microseconds(), 2300000); + EXPECT_GE(duration.total_microseconds(), + 2300000 - TIMEOUT_TOLERANCE); EXPECT_LE(duration.total_microseconds(), 2600000); // Test timeout shorter than 1s. @@ -310,7 +320,8 @@ TEST_F(IfaceMgrTest, receiveTimeout4) { ASSERT_FALSE(pkt); duration = stop_time - start_time; // Check if measured duration is within <0.4s; 0.7s>. - EXPECT_GE(duration.total_microseconds(), 400000); + EXPECT_GE(duration.total_microseconds(), + 400000 - TIMEOUT_TOLERANCE); EXPECT_LE(duration.total_microseconds(), 700000); // Test with invalid fractional timeout values. From 054304b9814bcda55e3c4b096376f14fb3f8c0ef Mon Sep 17 00:00:00 2001 From: Jelte Jansen Date: Mon, 17 Sep 2012 19:52:05 +0200 Subject: [PATCH 185/199] [2109] addressed review comments - fix doxygen and comments - inclusion order fix - remove dead line from makefile --- src/lib/datasrc/Makefile.am | 1 - .../datasrc/memory/tests/zone_finder_unittest.cc | 14 ++++++++++---- src/lib/datasrc/memory/zone_finder.cc | 2 +- 3 files changed, 11 insertions(+), 6 deletions(-) diff --git a/src/lib/datasrc/Makefile.am b/src/lib/datasrc/Makefile.am index aa93b774ac..bd96838d16 100644 --- a/src/lib/datasrc/Makefile.am +++ b/src/lib/datasrc/Makefile.am @@ -64,7 +64,6 @@ libb10_datasrc_la_LIBADD = $(top_builddir)/src/lib/exceptions/libb10-exceptions. libb10_datasrc_la_LIBADD += $(top_builddir)/src/lib/dns/libb10-dns++.la libb10_datasrc_la_LIBADD += $(top_builddir)/src/lib/log/libb10-log.la libb10_datasrc_la_LIBADD += $(top_builddir)/src/lib/cc/libb10-cc.la -#libb10_datasrc_la_LIBADD += memory/libdatasrc_memory.la # convenience library libb10_datasrc_la_LIBADD += $(SQLITE_LIBS) BUILT_SOURCES = datasrc_config.h datasrc_messages.h datasrc_messages.cc diff --git a/src/lib/datasrc/memory/tests/zone_finder_unittest.cc b/src/lib/datasrc/memory/tests/zone_finder_unittest.cc index 15fec597d8..08265f891e 100644 --- a/src/lib/datasrc/memory/tests/zone_finder_unittest.cc +++ b/src/lib/datasrc/memory/tests/zone_finder_unittest.cc @@ -13,12 +13,19 @@ // PERFORMANCE OF THIS SOFTWARE. #include "memory_segment_test.h" + +// NOTE: this faked_nsec3 inclusion (and all related code below) +// was ported during #2109 for the convenience of implementing #2218 +// In #2218 the NSEC3 test code in this file is expected to be finalized. +// In #2219 the original is expected to be removed, and this file should +// probably be moved here (and any leftover code not handled in #2218 should +// be cleaned up) #include "../../tests/faked_nsec3.h" -#include -#include #include #include +#include +#include #include @@ -57,6 +64,7 @@ const char* const zzz_hash = "R53BQ7CC2UVMUBFU5OCMM6PERS9TK9EN"; // A simple faked NSEC3 hash calculator with a dedicated creator for it. // // This is used in some NSEC3-related tests below. +// Also see NOTE at inclusion of "../../tests/faked_nsec3.h" class TestNSEC3HashCreator : public NSEC3HashCreator { class TestNSEC3Hash : public NSEC3Hash { private: @@ -685,8 +693,6 @@ TEST_F(InMemoryZoneFinderTest, glue) { * \brief Test searching. * * Check it finds or does not find correctly and does not throw exceptions. - * \todo This doesn't do any kind of CNAME and so on. If it isn't - * directly there, it just tells it doesn't exist. */ void InMemoryZoneFinderTest::findCheck(ZoneFinder::FindResultFlags expected_flags, diff --git a/src/lib/datasrc/memory/zone_finder.cc b/src/lib/datasrc/memory/zone_finder.cc index 994b0f98f3..3768602f44 100644 --- a/src/lib/datasrc/memory/zone_finder.cc +++ b/src/lib/datasrc/memory/zone_finder.cc @@ -44,7 +44,7 @@ namespace { /// \param rdataset The RdataSet to create the RRsetPtr for /// \param rrclass The RRClass as passed by the client /// -/// Returns an empty TreeNodeRRsetPtr is either node or rdataset is NULL. +/// Returns an empty TreeNodeRRsetPtr if node is NULL or if rdataset is NULL. TreeNodeRRsetPtr createTreeNodeRRset(const ZoneNode* node, const RdataSet* rdataset, From 547bcae62d8767bc92af6eafb396f454b344b593 Mon Sep 17 00:00:00 2001 From: JINMEI Tatuya Date: Mon, 17 Sep 2012 11:31:01 -0700 Subject: [PATCH 186/199] [master] cleanup: rm obsolete comments, rename a variable, some style cleanups regarding the variable name, "local_mem_sgmt_" is now awkward because it's now of the base class type. okayed on jabber. --- src/lib/datasrc/memory/memory_client.cc | 55 +++++++++++-------------- 1 file changed, 24 insertions(+), 31 deletions(-) diff --git a/src/lib/datasrc/memory/memory_client.cc b/src/lib/datasrc/memory/memory_client.cc index 1674095979..daeea1a162 100644 --- a/src/lib/datasrc/memory/memory_client.cc +++ b/src/lib/datasrc/memory/memory_client.cc @@ -87,28 +87,24 @@ private: }; public: - InMemoryClientImpl(util::MemorySegment& mem_sgmt, - RRClass rrclass) : - local_mem_sgmt_(mem_sgmt), + InMemoryClientImpl(util::MemorySegment& mem_sgmt, RRClass rrclass) : + mem_sgmt_(mem_sgmt), rrclass_(rrclass), zone_count_(0), - zone_table_(ZoneTable::create(local_mem_sgmt_, rrclass)), - file_name_tree_(FileNameTree::create(local_mem_sgmt_, false)) + zone_table_(ZoneTable::create(mem_sgmt_, rrclass)), + file_name_tree_(FileNameTree::create(mem_sgmt_, false)) {} ~InMemoryClientImpl() { FileNameDeleter deleter; - FileNameTree::destroy(local_mem_sgmt_, file_name_tree_, deleter); + FileNameTree::destroy(mem_sgmt_, file_name_tree_, deleter); - ZoneTable::destroy(local_mem_sgmt_, zone_table_, rrclass_); + ZoneTable::destroy(mem_sgmt_, zone_table_, rrclass_); // see above for the assert(). - assert(local_mem_sgmt_.allMemoryDeallocated()); + assert(mem_sgmt_.allMemoryDeallocated()); } - // Memory segment to allocate/deallocate memory for the zone table. - // (This will eventually have to be abstract; for now we hardcode the - // specific derived segment class). - util::MemorySegment& local_mem_sgmt_; + util::MemorySegment& mem_sgmt_; const RRClass rrclass_; unsigned int zone_count_; ZoneTable* zone_table_; @@ -155,14 +151,14 @@ public: // Ensure a separate level exists for the "wildcarding" name, // and mark the node as "wild". ZoneNode *node; - zone_data.insertName(local_mem_sgmt_, wname.split(1), &node); + zone_data.insertName(mem_sgmt_, wname.split(1), &node); node->setFlag(ZoneData::WILDCARD_NODE); // Ensure a separate level exists for the wildcard name. // Note: for 'name' itself we do this later anyway, but the // overhead should be marginal because wildcard names should // be rare. - zone_data.insertName(local_mem_sgmt_, wname, &node); + zone_data.insertName(mem_sgmt_, wname, &node); } } } @@ -311,7 +307,7 @@ public: NSEC3Data* nsec3_data = zone_data.getNSEC3Data(); if (nsec3_data == NULL) { - nsec3_data = NSEC3Data::create(local_mem_sgmt_, nsec3_rdata); + nsec3_data = NSEC3Data::create(mem_sgmt_, nsec3_rdata); zone_data.setNSEC3Data(nsec3_data); } else { size_t salt_len = nsec3_data->getSaltLen(); @@ -332,18 +328,17 @@ public: transform(fst_label.begin(), fst_label.end(), fst_label.begin(), ::toupper); - ZoneNode *node; - nsec3_data->insertName(local_mem_sgmt_, Name(fst_label), &node); + ZoneNode* node; + nsec3_data->insertName(mem_sgmt_, Name(fst_label), &node); RdataEncoder encoder; // We assume that rrsig has already been checked to match rrset // by the caller. - RdataSet *set = RdataSet::create(local_mem_sgmt_, encoder, - rrset, rrsig); - RdataSet *old_set = node->setData(set); + RdataSet* set = RdataSet::create(mem_sgmt_, encoder, rrset, rrsig); + RdataSet* old_set = node->setData(set); if (old_set != NULL) { - RdataSet::destroy(local_mem_sgmt_, rrclass_, old_set); + RdataSet::destroy(mem_sgmt_, rrclass_, old_set); } } @@ -393,8 +388,7 @@ public: addNSEC3(last_rrset_, rrsig, zone_data); } else { ZoneNode* node; - zone_data.insertName(local_mem_sgmt_, - last_rrset_->getName(), &node); + zone_data.insertName(mem_sgmt_, last_rrset_->getName(), &node); RdataSet* set = node->getData(); @@ -413,7 +407,7 @@ public: } RdataEncoder encoder; - RdataSet *new_set = RdataSet::create(local_mem_sgmt_, encoder, + RdataSet *new_set = RdataSet::create(mem_sgmt_, encoder, last_rrset_, rrsig); new_set->next = set; node->setData(new_set); @@ -442,7 +436,7 @@ public: NSEC3Data* nsec3_data = zone_data.getNSEC3Data(); if (nsec3_data == NULL) { - nsec3_data = NSEC3Data::create(local_mem_sgmt_, param); + nsec3_data = NSEC3Data::create(mem_sgmt_, param); zone_data.setNSEC3Data(nsec3_data); } else { size_t salt_len = nsec3_data->getSaltLen(); @@ -550,7 +544,7 @@ InMemoryClient::InMemoryClientImpl::load( boost::function rrset_installer) { SegmentObjectHolder holder( - local_mem_sgmt_, ZoneData::create(local_mem_sgmt_, zone_name), + mem_sgmt_, ZoneData::create(mem_sgmt_, zone_name), rrclass_); assert(!last_rrset_); @@ -595,8 +589,7 @@ InMemoryClient::InMemoryClientImpl::load( // Set the filename in file_name_tree_ now, so that getFileName() // can use it (during zone reloading). FileNameNode* node(NULL); - switch (file_name_tree_->insert(local_mem_sgmt_, - zone_name, &node)) { + switch (file_name_tree_->insert(mem_sgmt_, zone_name, &node)) { case FileNameTree::SUCCESS: case FileNameTree::ALREADYEXISTS: // These are OK @@ -611,8 +604,8 @@ InMemoryClient::InMemoryClientImpl::load( std::string* tstr = node->setData(new std::string(filename)); delete tstr; - ZoneTable::AddResult result(zone_table_->addZone(local_mem_sgmt_, - rrclass_, zone_name)); + ZoneTable::AddResult result(zone_table_->addZone(mem_sgmt_, rrclass_, + zone_name)); if (result.code == result::SUCCESS) { // Only increment the zone count if the zone doesn't already // exist. @@ -623,7 +616,7 @@ InMemoryClient::InMemoryClientImpl::load( holder.release())); assert(fr.code == result::SUCCESS); if (fr.zone_data != NULL) { - ZoneData::destroy(local_mem_sgmt_, fr.zone_data, rrclass_); + ZoneData::destroy(mem_sgmt_, fr.zone_data, rrclass_); } return (result.code); From 0f08167548c6a8618828d6e5d39ec0fbfc7edda0 Mon Sep 17 00:00:00 2001 From: JINMEI Tatuya Date: Mon, 17 Sep 2012 16:49:27 -0700 Subject: [PATCH 187/199] [master] doc cleanup: removed an obsolete description. TreeNodeRRset::getTTL() is now implemented, so "throw unconditionally" is simply wrong. should be trivial enough, directly committing. --- src/lib/datasrc/memory/treenode_rrset.h | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/lib/datasrc/memory/treenode_rrset.h b/src/lib/datasrc/memory/treenode_rrset.h index bbaeff516b..3f4d6d059b 100644 --- a/src/lib/datasrc/memory/treenode_rrset.h +++ b/src/lib/datasrc/memory/treenode_rrset.h @@ -156,8 +156,6 @@ public: } /// \brief Specialized version of \c getTTL() for \c TreeNodeRRset. - /// - /// It throws \c isc::Unexpected unconditionally. virtual const dns::RRTTL& getTTL() const; /// \brief Specialized version of \c setName() for \c TreeNodeRRset. From ccbb6bab9cd994d0a7168f2be74040919625e5c9 Mon Sep 17 00:00:00 2001 From: JINMEI Tatuya Date: Mon, 17 Sep 2012 17:06:23 -0700 Subject: [PATCH 188/199] [master] trivial style cleanup: position of operator '*' --- src/lib/datasrc/memory/memory_client.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib/datasrc/memory/memory_client.cc b/src/lib/datasrc/memory/memory_client.cc index daeea1a162..d70f34585d 100644 --- a/src/lib/datasrc/memory/memory_client.cc +++ b/src/lib/datasrc/memory/memory_client.cc @@ -150,7 +150,7 @@ public: // Ensure a separate level exists for the "wildcarding" name, // and mark the node as "wild". - ZoneNode *node; + ZoneNode* node; zone_data.insertName(mem_sgmt_, wname.split(1), &node); node->setFlag(ZoneData::WILDCARD_NODE); From a42c1338a4d27d0bd1d49131feecf49c450aed09 Mon Sep 17 00:00:00 2001 From: JINMEI Tatuya Date: Mon, 17 Sep 2012 18:13:12 -0700 Subject: [PATCH 189/199] [master] minor style fix: remove unnecessary and techinicaly obsolete 'static'. --- src/lib/datasrc/memory/tests/memory_client_unittest.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib/datasrc/memory/tests/memory_client_unittest.cc b/src/lib/datasrc/memory/tests/memory_client_unittest.cc index 6952a7175d..d5aa431786 100644 --- a/src/lib/datasrc/memory/tests/memory_client_unittest.cc +++ b/src/lib/datasrc/memory/tests/memory_client_unittest.cc @@ -66,7 +66,7 @@ private: size_t throw_count_; }; -static const char* rrset_data[] = { +const char* rrset_data[] = { "example.org. 3600 IN SOA ns1.example.org. bugs.x.w.example.org. 68 3600 300 3600000 3600", "a.example.org. 3600 IN A 192.168.0.1", "a.example.org. 3600 IN MX 10 mail.example.org.", From 2c05e5b0d8011671469ab20aa5c0c5f9325bc8f3 Mon Sep 17 00:00:00 2001 From: Naoki Kambe Date: Tue, 18 Sep 2012 11:49:57 +0900 Subject: [PATCH 190/199] [2158] updated counters handling in the classes - If it omits setting the counters, which are notifyoutv4, notifyoutv6, xfrrej, and xfrreqdone, they are set to None as defaults when the object is initiating. Then when each counter is invoked in some method, it checks whether the counter is Nonetype or not. Unless the counter is NoneType, it invokes the counter. After that unless the counter is callable, a TypeError exception would be raised. - added some tests for this changes - removed some dead code from xfrout.py.in --- src/bin/xfrout/tests/xfrout_test.py.in | 47 +++++++++++++++++++ src/bin/xfrout/xfrout.py.in | 20 ++++---- src/lib/python/isc/notify/notify_out.py | 16 +++---- .../isc/notify/tests/notify_out_test.py | 36 ++++++++++++++ 4 files changed, 99 insertions(+), 20 deletions(-) diff --git a/src/bin/xfrout/tests/xfrout_test.py.in b/src/bin/xfrout/tests/xfrout_test.py.in index 15ae2b42e4..9e075272d4 100644 --- a/src/bin/xfrout/tests/xfrout_test.py.in +++ b/src/bin/xfrout/tests/xfrout_test.py.in @@ -472,6 +472,25 @@ class TestXfroutSession(TestXfroutSessionBase): self.check_transfer_acl(acl_setter) self.assertEqual(self._zone_name_xfrrej, TEST_ZONE_NAME_STR) + def test_transfer_acl_with_nonetype_xfrrej(self): + # ACL checks only with the default ACL and NoneType xfrrej + # counter + def acl_setter(acl): + self.xfrsess._acl = acl + self.xfrsess._counter_xfrrej = None + self.assertIsNone(self._zone_name_xfrrej) + self.check_transfer_acl(acl_setter) + self.assertIsNone(self._zone_name_xfrrej) + + def test_transfer_acl_with_notcallable_xfrrej(self): + # ACL checks only with the default ACL and not callable xfrrej + # counter + def acl_setter(acl): + self.xfrsess._acl = acl + self.xfrsess._counter_xfrrej = 'NOT CALLABLE' + self.assertRaises(TypeError, + self.check_transfer_acl, acl_setter) + def test_transfer_zoneacl(self): # ACL check with a per zone ACL + default ACL. The per zone ACL # should match the queryied zone, so it should be used. @@ -853,6 +872,34 @@ class TestXfroutSession(TestXfroutSessionBase): self.assertEqual(self.sock.readsent(), b"success") self.assertEqual(self._zone_name_xfrreqdone, TEST_ZONE_NAME_STR) + def test_dns_xfrout_start_with_nonetype_xfrreqdone(self): + def noerror(msg, name, rrclass): + return Rcode.NOERROR() + self.xfrsess._xfrout_setup = noerror + + def myreply(msg, sock): + self.sock.send(b"success") + + self.assertIsNone(self._zone_name_xfrreqdone) + self.xfrsess._reply_xfrout_query = myreply + self.xfrsess._counter_xfrreqdone = None + self.xfrsess.dns_xfrout_start(self.sock, self.mdata) + self.assertIsNone(self._zone_name_xfrreqdone) + + def test_dns_xfrout_start_with_notcallable_xfrreqdone(self): + def noerror(msg, name, rrclass): + return Rcode.NOERROR() + self.xfrsess._xfrout_setup = noerror + + def myreply(msg, sock): + self.sock.send(b"success") + + self.xfrsess._reply_xfrout_query = myreply + self.xfrsess._counter_xfrreqdone = 'NOT CALLABLE' + self.assertRaises(TypeError, + self.xfrsess.dns_xfrout_start, self.sock, + self.mdata) + def test_reply_xfrout_query_axfr(self): self.xfrsess._soa = self.soa_rrset self.xfrsess._iterator = [self.soa_rrset] diff --git a/src/bin/xfrout/xfrout.py.in b/src/bin/xfrout/xfrout.py.in index 79ca537693..d3141ad05c 100755 --- a/src/bin/xfrout/xfrout.py.in +++ b/src/bin/xfrout/xfrout.py.in @@ -171,12 +171,8 @@ class XfroutSession(): self._jnl_reader = None # will be set to a reader for IXFR # Set counter handlers for counting Xfr requests. An argument # is required for zone name. - self._counter_xfrrej = lambda x: None - if hasattr(counter_xfrrej, '__call__'): - self._counter_xfrrej = counter_xfrrej - self._counter_xfrreqdone = lambda x: None - if hasattr(counter_xfrreqdone, '__call__'): - self._counter_xfrreqdone = counter_xfrreqdone + self._counter_xfrrej = counter_xfrrej + self._counter_xfrreqdone = counter_xfrreqdone self._handle() def create_tsig_ctx(self, tsig_record, tsig_key_ring): @@ -279,8 +275,9 @@ class XfroutSession(): format_zone_str(zone_name, zone_class)) return None, None elif acl_result == REJECT: - # count rejected Xfr request by each zone name - self._counter_xfrrej(zone_name.to_text()) + if self._counter_xfrrej is not None: + # count rejected Xfr request by each zone name + self._counter_xfrrej(zone_name.to_text()) logger.debug(DBG_XFROUT_TRACE, XFROUT_QUERY_REJECTED, self._request_type, format_addrinfo(self._remote), format_zone_str(zone_name, zone_class)) @@ -536,8 +533,9 @@ class XfroutSession(): except Exception as err: logger.error(XFROUT_XFR_TRANSFER_ERROR, self._request_typestr, format_addrinfo(self._remote), zone_str, err) - # count done Xfr requests by each zone name - self._counter_xfrreqdone(zone_name.to_text()) + if self._counter_xfrreqdone is not None: + # count done Xfr requests by each zone name + self._counter_xfrreqdone(zone_name.to_text()) logger.info(XFROUT_XFR_TRANSFER_DONE, self._request_typestr, format_addrinfo(self._remote), zone_str) @@ -1023,8 +1021,6 @@ class XfroutCounter: with self._lock: self._add_perzone_counter(zone_name) self._statistics_data[self.perzone_prefix][zone_name][counter_name] += step - #def __perzone_incrementer(zone_name, counter_name=item): - # self._perzone_incrementer(zone_name, counter_name) setattr(self, 'inc_%s' % item, __perzone_incrementer) diff --git a/src/lib/python/isc/notify/notify_out.py b/src/lib/python/isc/notify/notify_out.py index 8d632d06ef..46bb00b05c 100644 --- a/src/lib/python/isc/notify/notify_out.py +++ b/src/lib/python/isc/notify/notify_out.py @@ -145,12 +145,8 @@ class NotifyOut: self._nonblock_event = threading.Event() # Set counter handlers for counting notifies. An argument is # required for zone name. - self._counter_notifyoutv4 = lambda x: None - if hasattr(counter_notifyoutv4, '__call__'): - self._counter_notifyoutv4 = counter_notifyoutv4 - self._counter_notifyoutv6 = lambda x: None - if hasattr(counter_notifyoutv6, '__call__'): - self._counter_notifyoutv6 = counter_notifyoutv6 + self._counter_notifyoutv4 = counter_notifyoutv4 + self._counter_notifyoutv6 = counter_notifyoutv6 def _init_notify_out(self, datasrc_file): '''Get all the zones name and its notify target's address. @@ -488,9 +484,13 @@ class NotifyOut: sock = zone_notify_info.create_socket(addrinfo[0]) sock.sendto(render.get_data(), 0, addrinfo) # count notifying by IPv4 or IPv6 for statistics - if zone_notify_info.get_socket().family == socket.AF_INET: + if zone_notify_info.get_socket().family \ + == socket.AF_INET \ + and self._counter_notifyoutv4 is not None: self._counter_notifyoutv4(zone_notify_info.zone_name) - elif zone_notify_info.get_socket().family == socket.AF_INET6: + elif zone_notify_info.get_socket().family \ + == socket.AF_INET6 \ + and self._counter_notifyoutv6 is not None: self._counter_notifyoutv6(zone_notify_info.zone_name) logger.info(NOTIFY_OUT_SENDING_NOTIFY, addrinfo[0], addrinfo[1]) diff --git a/src/lib/python/isc/notify/tests/notify_out_test.py b/src/lib/python/isc/notify/tests/notify_out_test.py index 8343d0bf53..b9183e0396 100644 --- a/src/lib/python/isc/notify/tests/notify_out_test.py +++ b/src/lib/python/isc/notify/tests/notify_out_test.py @@ -289,6 +289,42 @@ class TestNotifyOut(unittest.TestCase): self.assertIsNone(self._notifiedv4_zone_name) self.assertEqual(self._notifiedv6_zone_name, 'example.net.') + def test_send_notify_message_udp_ipv4_with_nonetype_notifyoutv4(self): + example_com_info = self._notify._notify_infos[('example.net.', 'IN')] + example_com_info.prepare_notify_out() + self.assertIsNone(self._notifiedv4_zone_name) + self.assertIsNone(self._notifiedv6_zone_name) + self._notify._counter_notifyoutv4 = None + self._notify._send_notify_message_udp(example_com_info, + ('192.0.2.1', 53)) + self.assertIsNone(self._notifiedv4_zone_name) + self.assertIsNone(self._notifiedv6_zone_name) + + def test_send_notify_message_udp_ipv4_with_notcallable_notifyoutv4(self): + example_com_info = self._notify._notify_infos[('example.net.', 'IN')] + example_com_info.prepare_notify_out() + self._notify._counter_notifyoutv4 = 'NOT CALLABLE' + self.assertRaises(TypeError, + self._notify._send_notify_message_udp, + example_com_info, ('192.0.2.1', 53)) + + def test_send_notify_message_udp_ipv6_with_nonetype_notifyoutv6(self): + example_com_info = self._notify._notify_infos[('example.net.', 'IN')] + self.assertIsNone(self._notifiedv4_zone_name) + self.assertIsNone(self._notifiedv6_zone_name) + self._notify._counter_notifyoutv6 = None + self._notify._send_notify_message_udp(example_com_info, + ('2001:db8::53', 53)) + self.assertIsNone(self._notifiedv4_zone_name) + self.assertIsNone(self._notifiedv6_zone_name) + + def test_send_notify_message_udp_ipv6_with_notcallable_notifyoutv6(self): + example_com_info = self._notify._notify_infos[('example.net.', 'IN')] + self._notify._counter_notifyoutv6 = 'NOT CALLABLE' + self.assertRaises(TypeError, + self._notify._send_notify_message_udp, + example_com_info, ('2001:db8::53', 53)) + def test_send_notify_message_with_bogus_address(self): example_com_info = self._notify._notify_infos[('example.net.', 'IN')] From cb979cf26c3fa6e00810e9dbce4a80e9293974aa Mon Sep 17 00:00:00 2001 From: Marcin Siodelski Date: Tue, 18 Sep 2012 11:38:04 +0200 Subject: [PATCH 191/199] [2230] Removed dhcp-perf-guide.html. The dhcp-perf-guide.html is supposed to be created with make. --- tests/tools/dhcp-ubench/dhcp-perf-guide.html | 624 ------------------- 1 file changed, 624 deletions(-) delete mode 100644 tests/tools/dhcp-ubench/dhcp-perf-guide.html diff --git a/tests/tools/dhcp-ubench/dhcp-perf-guide.html b/tests/tools/dhcp-ubench/dhcp-perf-guide.html deleted file mode 100644 index 1dd7ce5845..0000000000 --- a/tests/tools/dhcp-ubench/dhcp-perf-guide.html +++ /dev/null @@ -1,624 +0,0 @@ - - - DHCP Performance Guide

    DHCP Performance Guide

    Tomasz Mrugalski

    Marcin Siodelski

    This is a companion document for BIND 10 version - 20120817.

    Abstract

    BIND 10 is a framework that features Domain Name System - (DNS) suite and Dynamic Host Configuration Protocol (DHCP) - servers with development managed by Internet Systems Consortium (ISC). - This document describes various aspects of DHCP performance, - measurements and tuning. It covers BIND 10 DHCP (codename Kea), - existing ISC DHCP4 software, perfdhcp (a DHCP performance - measurement tool) and other related topics.


    Preface

    Table of Contents

    Acknowledgements

    Acknowledgements

    ISC would like to acknowledge generous support for - BIND 10 development of DHCPv4 and DHCPv6 components provided - by Comcast.

    Chapter 1. Introduction

    - This document is in its early stages of development. It is - expected to grow significantly in a near future. It will - cover topics like database backend perfomance measurements, - pros an cons of various optimization techniques and - tools. -

    Chapter 2. ISC DHCP 4.x

    - TODO: Write something about ISC DHCP4 here. -

    Chapter 3. Kea

    - -

    Backend performance evaluation

    - Kea will support several different database backends, using - both popular databases (like MySQL or SQLite) and - custom-developed solutions (like in-memory database). BIND 10 - source code features set of performance microbenchmarks. - These are small tools written in C/C++ that simulate expected - DHCP server behaviour and evaluate the performance of - considered databases. As implemented benchmarks are not really - simulating DHCP operation, but rather use set of primitives - that can be used by a real server, they are called - micro-benchmarks. -

    Although there are many operations and data types that - server could store in a database, the most frequently used data - type is lease information. Although lease information for IPv4 - and IPv6 differs slightly, it is expected that the performance - differences will be minimal between IPv4 and IPv6 lease operations. - Therefore each test uses lease4 table for performance measurements. -

    All benchmarks are implemented as single threaded applications - that take advantage of a single database connection.

    - Those benchmarks are stored in tests/tools/dhcp-ubench - directory. This directory contains simplified prototypes for - various DB back-ends that are planned or considered as a - backend engine for BIND10 DHCP. Athough trivial now, they are - expected to evolve into useful tools that will allow users to - measure performance in their specific environment. -

    - Currently the following benchmarks are implemented: -

    • in memory+flat file

    • SQLite

    • MySQL

    -

    - As they require additional (sometimes heavy) dependencies, they are not - built by default. Actually, their build system is completely separated. - It will be eventually merged with the main BIND10 makefile system, but - that is a low priority for now. -

    - All benchmarks will follow the same pattern: -

    1. prepare operation (connect to a database, create a file etc.)

    2. Measure timestamp 0

    3. Commit new lease4 (repeated X times)

    4. Measure timestamp 1

    5. Search for random lease4 (repeated X times)

    6. Measure timestamp 2

    7. Update existing lease4 (repeated X times)

    8. Measure timestamp 3

    9. Delete existing lease4 (repeated X times)

    10. Measure timestamp 4

    11. Print out statistics, based on X and measured timestamps.

    - - Although this approach does not attempt to simulate actual DHCP server - operation that has mix of all steps intervening, it answers the - questions about basic database strenghts and weak points. In particular - it can show what is the impact of specific DB optimizations, like - changing engine, optimizing for writes/reads etc. -

    - The framework attempts to do the same amount of operations for every - backend thus allowing fair complarison between them. -

    MySQL backend

    MySQL backend requires MySQL client development libraries. It uses - mysql_config tool (that works similar to pkg-config) to discover required - compilation and linking options. To install required packages on Ubuntu, - use the following command: - -

    $ sudo apt-get install mysql-client mysql-server libmysqlclient-dev

    - - Make sure that MySQL server is running. Make sure that you have your setup - configured so there is a user that is able to modify used database.

    Before running tests, you need to initialize your database. You can - use mysql.schema script for that purpose. WARNING: It will drop existing - Kea database. Do not run this on your production server. Assuming your - MySQL user is kea, you can initialize your test database by: - -

    $ mysql -u kea -p < mysql.schema

    -

    After database is initialized, you are ready to run the test: -

    $ ./mysql_ubench

    - - or - -

    $ ./mysql_ubench > results->mysql.txt

    - - Redirecting output to a file is important, because for each operation - there is a single character printed to show progress. If you have a slow - terminal, this may considerably affect test perfromance. On the other hand, - printing something after each operation is required, as poor DB setting - may slow down operations to around 20 per second. Observant user is expected - to note that initial dots are printed too slowly and abort the test.

    Currently all default parameters are hardcoded. Default values can be - overwritten using command line switches. Although all benchmarks take - the same list of parameters, some of them are specific to a given backend - type. To get a list of supported parameters, run your benchmark with -h option: - -

    $ ./mysql_ubench -h
    -This is a benchmark designed to measure expected performance
    -of several backends. This particular version identifies itself
    -as following:
    -MySQL client version is 5.5.24
    -
    -Possible command-line parameters:
    - -h - help (you are reading this)
    - -m hostname - specifies MySQL server to connect (MySQL backend only)
    - -u username - specifies MySQL user name (MySQL backend only)
    - -p password - specifies MySQL passwod (MySQL backend only)
    - -f name - database or filename (MySQL, SQLite and memfile)
    - -n integer - number of test repetitions (MySQL, SQLite and memfile)
    - -s yes|no - synchronous/asynchronous operation (MySQL, SQLite and memfile)
    - -v yes|no - verbose mode (MySQL, SQLite and memfile)
    - -c yes|no - should compiled statements be used (MySQL only)
    -

    - -

    MySQL tweaks

    One parameter that has huge impact on performance is a a backend engine. - You can get a list of engines of your MySQL implementation by using - -

    > show engines;

    - - in your mysql client. Two notable engines are MyISAM and InnoDB. mysql_ubench will - use MyISAM for synchronous mode and InnoDB for asynchronous.

    SQLite-ubench

    SQLite backend requires both sqlite3 development and run-time package. Their - names may vary from system to system, but on Ubuntu 12.04 they are called - sqlite3 libsqlite3-dev. To install them, use the following command: - -

    > sudo apt-get install sqlite3 libsqlite3-dev

    - - Before running the test the database has to be created. Use the following command for that: -

    > cat sqlite.schema | sqlite3 sqlite.db

    - - A new database called sqlite.db will be created. That is the default name used - by sqlite_ubench test. If you prefer other name, make sure you update - sqlite_ubench.cc accordingly.

    Once the database is created, you can run tests: -

    > ./sqlite_ubench

    - or -

    > ./sqlite_ubench > results-sqlite.txt

    -

    SQLite tweaks

    To modify default sqlite_ubench parameters, command line - switches can be used. Currently supported parameters are - (default values specified in brackets): -

    1. -f filename - name of the database file ("sqlite.db")

    2. -n num - number of iterations (100)

    3. -s yes|no - should the operations be performend in synchronous (yes) - or asynchronous (no) manner (yes)

    4. -v yes|no - verbose mode. Should the test print out progress? (yes)

    5. -c yes|no - compiled statements. Should the SQL statements be precompiled?

    -

    SQLite can run in asynchronous or synchronous mode. This - mode can be controlled by using sync parameter. It is set - using (PRAGMA synchronous = ON or OFF).

    Another tweakable feature is journal mode. It can be - turned to several modes of operation. Its value can be - modified in SQLite_uBenchmark::connect(). See - http://www.sqlite.org/pragma.html#pragma_journal_mode for - detailed explanantion.

    memfile-ubench

    Memfile backend is custom developed prototype backend that - somewhat mimics operation of ISC DHCP4. It uses in-memory - storage using standard C++ and boost mechanisms (std::map and - boost::shared_ptr<>). All database changes are also - written to a lease file. That file is strictly write-only. This - approach takes advantage of the fact that simple append is faster - than edition with potential whole file relocation.

    memfile tweaks

    To modify default memfile_ubench parameters, command line - switches can be used. Currently supported parameters are - (default values specified in brackets): -

    1. -f filename - name of the database file ("dhcpd.leases")

    2. -n num - number of iterations (100)

    3. -s yes|no - should the operations be performend in synchronous (yes) - or asynchronous (no) manner (yes)

    4. -v yes|no - verbose mode. Should the test print out progress? (yes)

    -

    memfile can run in asynchronous or synchronous mode. This - mode can be controlled by using sync parameter. It uses - fflush() and fsync() in synchronous mode to make sure that - data is not buffered and physically stored on disk.

    Performance measurements

    This section contains sample results for backend performance measurements, - taken using microbenchmarks. Tests were conducted on reasonably powerful machine: -

    -CPU: Quad-core Intel(R) Core(TM) i7-2600K CPU @ 3.40GHz (8 logical cores)
    -HDD: 1,5TB Seagate Barracuda ST31500341AS 7200rpm (used only one of them), ext4 partition
    -OS: Ubuntu 12.04, running kernel 3.2.0-26-generic SMP x86_64
    -compiler: g++ (Ubuntu/Linaro 4.6.3-1ubuntu5) 4.6.3
    -MySQL version: 5.5.24
    -SQLite version: 3.7.9sourceid version is 2011-11-01 00:52:41 c7c6050ef060877ebe77b41d959e9df13f8c9b5e

    -

    Benchmarks were run in two series: synchronous and - asynchronous. As those modes offer radically different - performances, synchronous mode was conducted for 1000 (one - thousand) repetitions and asynchronous mode was conducted for - 100000 (hundred thousand) repetitions.

    Table 3.1. Synchronous results

    BackendOperationsCreateSearchUpdateDeleteAverage
    MySQL100031.603978s 0.116612s27.964191s27.695209s21.844998s
    SQLite100061.421356s 0.033283s59.476638s56.034150s44.241357s
    memfile100041.711886s 0.000724s42.267578s42.169679s31.537467s

    Following parameters were measured for asynchronous mode. - MySQL and SQLite were run with 100 thousand repetitions. Memfile - was run for 1 million repetitions due to much larger performance.

    Table 3.2. Asynchronous results

    BackendOperationsCreate [s]Search [s]Update [s]Delete [s]Average [s]
    MySQL10000010.584842s10.386402s10.062384s 8.890197s 9.980956s
    SQLite100000 3.710356s 3.159129s 2.865354s 2.439406s 3.043561s
    memfile1000000 (sic!) 6.084131s 0.862667s 6.018585s 5.146704s 4.528022s

    Presented performance results can be computed into operations per second metrics. - It should be noted that due to large differences between various operations (sometime - over 3 orders of magnitude), it is difficult to create a simple, readable chart with - that data.

    Table 3.3. Estimated performance

    BackendCreate [oper/s]Search [oper/s]Update [oper/s]Delete [oper/s]Average [oper/s]
    MySQL (async)9447.479627.979938.0011248.3410065.45
    SQLite (async)26951.5931654.2934899.7040993.5933624.79
    memfile (async)164362.011159195.84166152.01194299.11421002.24
    MySQL (sync)31.648575.4535.7636.112169.74
    SQLite (sync)16.2820045.3716.8117.857524.08
    memfile (sync)23.971381215.4723.6623.71345321.70

    Performance measurements

    Graphical representation of the performance results - presented in table Table 3.3, “Estimated performance”.

    Possible further optimizations

    - For debugging purposes the code was compiled with -g -O0 - flags. While majority of the time was spent in backend - functions (that was probably compiled with -O2 flags), the - benchmark code could perform faster, when compiled with -O2, - rather than -O0. That is expected to affect memfile benchmark. -

    - Currently all operations were conducted on one by one - basis. Each operation was treated as a separate - transaction. Grouping X operations together will potentially - bring almost X fold increase in synchronous operations. - Extension for this benchmark in this regard should be considered. - That affects only write operations (insert, update and delete). Read - operations (search) are expected to be barely affected. -

    - Multi-threaded or multi-process benchmark may be considered in - the future. It may be somewhat difficult as only some backends - support concurrent access. -

    Chapter 4. perfdhcp

    Purpose

    - Evaluation of the performance of a DHCP server requires that it - be tested under varying traffic loads. perfdhcp is a testing - tool with the capability to create traffic loads - and generate statistics from the results. Additional features, - such as the ability to send customised DHCP packets, allow it to - be used in a wide range of functional testing. -

    Key features

    - perfdhcp has a number of command line switches to - control DHCP message exchanges. Currently they fall into - the following categories: -

    • - Rate control - control how many DHCP exchanges - are initiated within a period of time. The tool can also simulate - best effort conditions by attempting to initiate as many DHCP - packet exchanges as possible within a unit of time. -

    • - Test exit specifiers - control the conditions for test - completion, including the number of initiated exchanges, - the test period orthe maximum number of dropped packets. -

    • - Packet templates - specify files containing packet templates that - are used by perfdhcp to create custom DHCP messages. The tool - allows the specification of a number of values indicating - offsets of values within a packet that are set by the tool. -

    • - Reporting - for each test produce a set of performance data - including the achieved packet exchange rate (server performance). - There are a number of diagnostic selectors available that - enable periodic (intermediate) reporting, printing of packet timestamps, - and the listing of detailed information about internal perfdhcp - states (for debugging). -

    • - Different mode of operations - specify the DHCP protocol used - (v4 or v6), two-way or four-way exchanges, use of the - Rapid Commit option for DHCPv6. -

    • - IP layer options - specify the local/remote address, local interface - and local port to be used for communication with DHCP server. -

    -

    Command line options

    - The following "help" output from the tool describes the - command line switches. This summary also lists the tool's - possible exit codes as well as describing the - error counters printed when the test is complete: -

    $ ./perfdhcp -h
    -perfdhcp [-hv] [-4|-6] [-r<rate>] [-t<report>] [-R<range>] [-b<base>]
    -    [-n<num-request>] [-p<test-period>] [-d<drop-time>] [-D<max-drop>]
    -    [-l<local-addr|interface>] [-P<preload>] [-a<aggressivity>]
    -    [-L<local-port>] [-s<seed>] [-i] [-B] [-c] [-1]
    -    [-T<template-file>] [-X<xid-offset>] [-O<random-offset]
    -    [-E<time-offset>] [-S<srvid-offset>] [-I<ip-offset>]
    -    [-x<diagnostic-selector>] [-w<wrapped>] [server]
    -
    -The [server] argument is the name/address of the DHCP server to
    -contact.  For DHCPv4 operation, exchanges are initiated by
    -transmitting a DHCP DISCOVER to this address.
    -
    -For DHCPv6 operation, exchanges are initiated by transmitting a DHCP
    -SOLICIT to this address.  In the DHCPv6 case, the special name 'all'
    -can be used to refer to All_DHCP_Relay_Agents_and_Servers (the
    -multicast address FF02::1:2), or the special name 'servers' to refer
    -to All_DHCP_Servers (the multicast address FF05::1:3).  The [server]
    -argument is optional only in the case that -l is used to specify an
    -interface, in which case [server] defaults to 'all'.
    -
    -The default is to perform a single 4-way exchange, effectively pinging
    -the server.
    -The -r option is used to set up a performance test, without
    -it exchanges are initiated as fast as possible.
    -
    -Options:
    --1: Take the server-ID option from the first received message.
    --4: DHCPv4 operation (default). This is incompatible with the -6 option.
    --6: DHCPv6 operation. This is incompatible with the -4 option.
    --a<aggressivity>: When the target sending rate is not yet reached,
    -    control how many exchanges are initiated before the next pause.
    --b<base>: The base mac, duid, IP, etc, used to simulate different
    -    clients.  This can be specified multiple times, each instance is
    -    in the <type>=<value> form, for instance:
    -    (and default) mac=00:0c:01:02:03:04.
    --d<drop-time>: Specify the time after which a request is treated as
    -    having been lost.  The value is given in seconds and may contain a
    -    fractional component.  The default is 1 second.
    --E<time-offset>: Offset of the (DHCPv4) secs field / (DHCPv6)
    -    elapsed-time option in the (second/request) template.
    -    The value 0 disables it.
    --h: Print this help.
    --i: Do only the initial part of an exchange: DO or SA, depending on
    -    whether -6 is given.
    --I<ip-offset>: Offset of the (DHCPv4) IP address in the requested-IP
    -    option / (DHCPv6) IA_NA option in the (second/request) template.
    --l<local-addr|interface>: For DHCPv4 operation, specify the local
    -    hostname/address to use when communicating with the server.  By
    -    default, the interface address through which traffic would
    -    normally be routed to the server is used.
    -    For DHCPv6 operation, specify the name of the network interface
    -    via which exchanges are initiated.
    --L<local-port>: Specify the local port to use
    -    (the value 0 means to use the default).
    --O<random-offset>: Offset of the last octet to randomize in the template.
    --P<preload>: Initiate first <preload> exchanges back to back at startup.
    --r<rate>: Initiate <rate> DORA/SARR (or if -i is given, DO/SA)
    -    exchanges per second.  A periodic report is generated showing the
    -    number of exchanges which were not completed, as well as the
    -    average response latency.  The program continues until
    -    interrupted, at which point a final report is generated.
    --R<range>: Specify how many different clients are used. With 1
    -    (the default), all requests seem to come from the same client.
    --s<seed>: Specify the seed for randomization, making it repeatable.
    --S<srvid-offset>: Offset of the server-ID option in the
    -    (second/request) template.
    --T<template-file>: The name of a file containing the template to use
    -    as a stream of hexadecimal digits.
    --v: Report the version number of this program.
    --w<wrapped>: Command to call with start/stop at the beginning/end of
    -    the program.
    --x<diagnostic-selector>: Include extended diagnostics in the output.
    -    <diagnostic-selector> is a string of single-keywords specifying
    -    the operations for which verbose output is desired.  The selector
    -    keyletters are:
    -   * 'a': print the decoded command line arguments
    -   * 'e': print the exit reason
    -   * 'i': print rate processing details
    -   * 'r': print randomization details
    -   * 's': print first server-id
    -   * 't': when finished, print timers of all successful exchanges
    -   * 'T': when finished, print templates
    --X<xid-offset>: Transaction ID (aka. xid) offset in the template.
    -
    -DHCPv4 only options:
    --B: Force broadcast handling.
    -
    -DHCPv6 only options:
    --c: Add a rapid commit option (exchanges will be SA).
    -
    -The remaining options are used only in conjunction with -r:
    -
    --D<max-drop>: Abort the test if more than <max-drop> requests have
    -    been dropped.  Use -D0 to abort if even a single request has been
    -    dropped.  If <max-drop> includes the suffix '%', it specifies a
    -    maximum percentage of requests that may be dropped before abort.
    -    In this case, testing of the threshold begins after 10 requests
    -    have been expected to be received.
    --n<num-request>: Initiate <num-request> transactions.  No report is
    -    generated until all transactions have been initiated/waited-for,
    -    after which a report is generated and the program terminates.
    --p<test-period>: Send requests for the given test period, which is
    -    specified in the same manner as -d.  This can be used as an
    -    alternative to -n, or both options can be given, in which case the
    -    testing is completed when either limit is reached.
    --t<report>: Delay in seconds between two periodic reports.
    -
    -Errors:
    -- tooshort: received a too short message
    -- orphans: received a message which doesn't match an exchange
    -   (duplicate, late or not related)
    -- locallimit: reached to local system limits when sending a message.
    -
    -Exit status:
    -The exit status is:
    -0 on complete success.
    -1 for a general error.
    -2 if an error is found in the command line arguments.
    -3 if there are no general failures in operation, but one or more
    -  exchanges are not successfully completed.
    -
    -        

    -

    Starting perfdhcp

    - In order to run a performance test, at least two separate systems - have to be installed: client and server. The first one must have - perfdhcp installed, and the latter must be running the DHCP server - (either v4 or v6). If only single system is available the client - and server can be run on virtual machines (running on the same - physical system) but in this case performance data may be heavily - impacted by the overhead involved in running such the virtual - machines. -

    - Currently, perfdhcp is seen from the server perspective as relay agent. - This simplifies its implementation: specifically there is no need to - receive traffic sent to braodcast addresses. However, it does impose - a requirement that the IPv4 - address has to be set manually on the interface that will be used to - communicate with the server. For example, if the DHCPv4 server is listening - on the interface connected to the 172.16.1.0 subnet, the interface on client - machine has to have network address assigned from the same subnet, e.g. -

    #ifconfig eth3 172.16.1.2. netmask 255.255.255.0 up

    -

    - As DHCP uses low port numbers (67 for DHCPv4 relays and - 547 for DHCPv6), running perfdhcp with non-root privileges will - usually result in the error message similar to this: -

    $./perfdhcp -4 -l eth3 -r 100 all
    -Error running perfdhcp: Failed to bind socket 3 to 172.16.1.2/port=67
    -        

    - The '-L' command line switch allows the use of a custom local port. - However, although the following command line will work: -

    $./perfdhcp -4 -l eth3 -r 100 -L 10067 all

    - in the standard configuration no responses will be received - from the DHCP server because the server responds to default relay - port 67. A way to overcome this issue is to run - perfdhcp as root. -

    perfdhcp command line examples

    - In this section, a number of perfdhcp command line examples - are presented as a quick start guide for new users. For the - detailed list of command line options refer to - the section called “Command line options”. -

    Example: basic usage

    - If server is listening on interface with IPv4 address 172.16.1.1, - the simplest perfdhcp command line will look like: -

    #./perfdhcp 172.16.1.1
    -***Rate statistics***
    -Rate: 206.345
    -
    -***Statistics for: DISCOVER-OFFER***
    -sent packets: 21641
    -received packets: 350
    -drops: 21291
    -orphans: 0
    -
    -min delay: 9.022 ms
    -avg delay: 143.100 ms
    -max delay: 259.303 ms
    -std deviation: 56.074 ms
    -collected packets: 30
    -
    -***Statistics for: REQUEST-ACK***
    -sent packets: 350
    -received packets: 268
    -drops: 82
    -orphans: 0
    -
    -min delay: 3.010 ms
    -avg delay: 152.470 ms
    -max delay: 258.634 ms
    -std deviation: 56.936 ms
    -collected packets: 0
    -          

    - Here, perfdhcp uses remote address 172.16.1.1 as a - destination address and will use a suitable local interface for - communication. Since, no rate control parameters have been specified, - it will initiate DHCP exchanges at the maximum possible rate. Due to the server's - performance limitation, it is likely that many of the packets will be dropped. - The performance test will continue running until it is - interrupted by the user (with ^C). -

    - The default performance statistics reported by perfdhcp have the - following meaning: -

    • Rate - number of packet exchanges (packet sent - to the server and matching response received from the server) - completed within a second.

    • sent packets - total number of DHCP packets of - a specific type sent to the server.

    • received packets - total number of DHCP packets - of specific type received from the server.

    • drops - number of dropped packets for the - particular exchange. The number of dropped packets is calculated as - the difference between the number of sent packets and number of - response packets received from the server. In some cases, the - server will have sent a reponse but perfdhcp execution ended before - the reponse arrived. In such case this packet will be counted - as dropped.

    • orphans - number of packets that have been - received from the server and did not match any packet sent by - perfdhcp. This may occur if received packet has been sent - to some other host or if then exchange timed out and - the sent packet was removed from perfdhcp's list of packets - awaiting a response.

    • min delay - minimum delay that occured between - sending the packet to the server and receiving a reponse from - it.

    • avg delay - average delay between sending the - packet of the specific type the server and receiving a response - from it.

    • max delay - maximum delay that occured between - sending the packet to the server and receiving a response from - it.

    • std deviation - standard deviation of the delay - between sending the packet of a specific type to the server and - receiving response from it.

    • collected packets - number of sent packets that - were garbage collected. Packets may get garbage collected when - the waiting time for server a response exceeds value set with the - '-d' (drop time) switch. - -d<drop-time>.

    -

    - Note: should multiple interfaces on the system running perfdhcp be - connected to the same subnet, the interface to be used for the test - can be specified using either the interface name: -

    #./perfdhcp -l eth3

    - or a local address assigned to it: -

    #./perfdhcp -l 172.16.1.2

    -

    Example: rate control

    - In the examples above perfdhcp initiates new exchanges with a best - effort rate. With this setting, many packets are expected to be dropped - by the server due to performance limitations. In many cases though, it is - desired to verify that the server can handle an expected (reasonable) rate - without dropping any packets. The following command is an example of such - a test: it causes perfdhcp to initiate 300 four-way exchanges - per second, and runs the test for 60 seconds: -

    #./perfdhcp -l eth3 -p 60 -r 300
    -***Rate statistics***
    -Rate: 256.683 exchanges/second, expected rate: 300 exchanges/second
    -
    -***Statistics for: DISCOVER-OFFER***
    -sent packets: 17783
    -received packets: 15401
    -drops: 2382
    -orphans: 0
    -
    -min delay: 0.109 ms
    -avg delay: 75.756 ms
    -max delay: 575.614 ms
    -std deviation: 60.513 ms
    -collected packets: 11
    -
    -***Statistics for: REQUEST-ACK***
    -sent packets: 15401
    -received packets: 15317
    -drops: 84
    -orphans: 0
    -
    -min delay: 0.536 ms
    -avg delay: 72.072 ms
    -max delay: 576.749 ms
    -std deviation: 58.189 ms
    -collected packets: 0
    -          

    - Note that here, the packet drops for the DISCOVER-OFFER - exchange have been significantly reduced (when compared with the - output from the previous example) thanks to the setting of a - reasonable rate. The non-zero number of packet drops and achieved - rate (256/s) indicate that server's measured performance is lower than 300 leases - per second. A further rate decrease should eliminate most of the packet - drops and bring the achieved rate close to expected rate: -

    #./perfdhcp -l eth3 -p 60 -r 100 -R 30
    -***Rate statistics***
    -Rate: 99.8164 exchanges/second, expected rate: 100 exchanges/second
    -
    -***Statistics for: DISCOVER-OFFER***
    -sent packets: 5989
    -received packets: 5989
    -drops: 0
    -orphans: 0
    -
    -min delay: 0.023 ms
    -avg delay: 2.198 ms
    -max delay: 181.760 ms
    -std deviation: 9.429 ms
    -collected packets: 0
    -
    -***Statistics for: REQUEST-ACK***
    -sent packets: 5989
    -received packets: 5989
    -drops: 0
    -orphans: 0
    -
    -min delay: 0.473 ms
    -avg delay: 2.355 ms
    -max delay: 189.658 ms
    -std deviation: 5.876 ms
    -collected packets: 0
    -          

    - There are now no packet drops, confirming that the server is able to - handle a load of 100 leases/second. - Note that the last parameter (-R 30) configures perfdhcp to simulate - traffic from 30 distinct clients. -

    Example: templates

    - By default the DHCP messages are formed with default options. With - template files, it is possible to define a custom packet format. -

    - The template file names are specified with -T<template-file> - command line option. This option can be specified zero, one or two times. - The first occurence of this option refers to DISCOVER or SOLICIT message - and the second occurence refers to REQUEST (DHCPv4 or DHCPv6) message. - If -T option occurs only once the DISCOVER or SOLICIT message will be - created from the template and the REQUEST message will be created - dynamically (without the template). Note that each template file - holds data for exactly one DHCP message type. Templates for multiple - message types must not be combined in the single file. - The content in template files is encoded in hexadecimal format. - perfdhcp forms the packet by replacing parts of the message buffer read - from the file with variable data such as elapsed time, hardware address, DUID - etc. The offsets where such variable data is placed is specific to the - template file and have to be specified from the command line. Refer to - the section called “Command line options” to find out how to - specify offsets for particular options and fields. With the following - command line the DHCPv6 SOLICIT and REQUEST packets will be formed from - solicit.hex and request6.hex packets: -

    #./perfdhcp -6 -l eth3 -r 100 -R 20 -T solicit.hex -T request6.hex -O 21 -E 84 -S 22 -I 40 servers
    -***Rate statistics***
    -Rate: 99.5398 exchanges/second, expected rate: 100 exchanges/second
    -
    -***Statistics for: SOLICIT-ADVERTISE***
    -sent packets: 570
    -received packets: 569
    -drops: 1
    -orphans: 0
    -
    -min delay: 0.259 ms
    -avg delay: 0.912 ms
    -max delay: 6.979 ms
    -std deviation: 0.709 ms
    -collected packets: 0
    -
    -***Statistics for: REQUEST-REPLY***
    -sent packets: 569
    -received packets: 569
    -drops: 0
    -orphans: 0
    -
    -min delay: 0.084 ms
    -avg delay: 0.607 ms
    -max delay: 6.490 ms
    -std deviation: 0.518 ms
    -collected packets: 0
    -          

    - where the switches have the following meaning: -

    • two occurences of -O 21 - DUID's last octet - positions in SOLICIT and REQUEST respectively.

    • -E 84 - elapsed time option position in the - REQUEST template

    • -S 22 - server id position in the REQUEST - template

    • -I 40 - IA_NA option position in the REQUEST - template

    -

    - The offsets of options indicate where they begin in the packet. - The only exception from this rule is -O<random-offset> - option that specifies the end of the DUID (DHCPv6) or MAC address - (DHCPv4). Depending on the number of simulated clients - (see -R<random-range> command line option) perfdhcp - will be randomizing bytes in packet buffer starting from this - position backwards. For the number of simulated clients - <= 256 only one octet (at random-offset position) - will be ranomized, for the number of clients <= 65536 - two octets (at random-offset and random-offset-1) - will be randmized etc. -

    \ No newline at end of file From b88349ce7d971514e9b6678d2c2eeb96c2a2a9b7 Mon Sep 17 00:00:00 2001 From: Naoki Kambe Date: Tue, 18 Sep 2012 20:41:44 +0900 Subject: [PATCH 192/199] [master] update a git-id for merge of #2158 --- ChangeLog | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ChangeLog b/ChangeLog index 5f0f7d278c..730ceb9c18 100644 --- a/ChangeLog +++ b/ChangeLog @@ -3,7 +3,7 @@ xfrreqdone. These are per-zone type counters. The value of these counters can be seen with zone name by invoking "Stats show Xfrout" via bindctl. - (Trac #2158, git TBD) + (Trac #2158, git e68c127fed52e6034ab5309ddd506da03c37a08a) 474. [func] stephen DHCP servers now use the BIND 10 logging system for messages. From 041dbdee3f1ab76ddb427cd9029fb1a827f365bd Mon Sep 17 00:00:00 2001 From: "Jeremy C. Reed" Date: Tue, 18 Sep 2012 12:50:08 -0500 Subject: [PATCH 193/199] [master] fix spelling typo in output I didn't get this reviewed, but make check still worked for me. --- src/lib/dhcp/tests/iface_mgr_unittest.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib/dhcp/tests/iface_mgr_unittest.cc b/src/lib/dhcp/tests/iface_mgr_unittest.cc index 3bf5ca4450..4fe7e23c30 100644 --- a/src/lib/dhcp/tests/iface_mgr_unittest.cc +++ b/src/lib/dhcp/tests/iface_mgr_unittest.cc @@ -1180,7 +1180,7 @@ TEST_F(IfaceMgrTest, DISABLED_detectIfaces_linux) { << " address on " << detected->getFullName() << " interface." << endl; FAIL(); } - cout << "Address " << addr->toText() << " on iterface " << detected->getFullName() + cout << "Address " << addr->toText() << " on interface " << detected->getFullName() << " matched with 'ifconfig -a' output." << endl; } } From b82ca7c8a4c719c5fb5cb94c5020d5fab5da30eb Mon Sep 17 00:00:00 2001 From: Mukund Sivaraman Date: Wed, 19 Sep 2012 15:37:16 +0530 Subject: [PATCH 194/199] [master] Cleanup a Makefile.am (which didn't link to libb10-dhcp++) Also move GTEST_LDADD to the bottom of the list of LDADDs. --- src/lib/dhcp/tests/Makefile.am | 23 ++++++++++------------- 1 file changed, 10 insertions(+), 13 deletions(-) diff --git a/src/lib/dhcp/tests/Makefile.am b/src/lib/dhcp/tests/Makefile.am index eba7e81f14..9e00ab0611 100644 --- a/src/lib/dhcp/tests/Makefile.am +++ b/src/lib/dhcp/tests/Makefile.am @@ -26,19 +26,15 @@ TESTS = if HAVE_GTEST TESTS += libdhcp++_unittests libdhcp___unittests_SOURCES = run_unittests.cc -libdhcp___unittests_SOURCES += ../libdhcp++.h ../libdhcp++.cc libdhcp___unittests_SOURCES += libdhcp++_unittest.cc -libdhcp___unittests_SOURCES += ../iface_mgr.cc ../iface_mgr.h iface_mgr_unittest.cc -libdhcp___unittests_SOURCES += ../iface_mgr_linux.cc -libdhcp___unittests_SOURCES += ../iface_mgr_bsd.cc -libdhcp___unittests_SOURCES += ../iface_mgr_sun.cc -libdhcp___unittests_SOURCES += ../option6_iaaddr.h ../option6_iaaddr.cc option6_iaaddr_unittest.cc -libdhcp___unittests_SOURCES += ../option6_ia.h ../option6_ia.cc option6_ia_unittest.cc -libdhcp___unittests_SOURCES += ../option6_addrlst.h ../option6_addrlst.cc option6_addrlst_unittest.cc -libdhcp___unittests_SOURCES += ../option4_addrlst.cc ../option4_addrlst.h option4_addrlst_unittest.cc -libdhcp___unittests_SOURCES += ../option.h ../option.cc option_unittest.cc -libdhcp___unittests_SOURCES += ../pkt6.h ../pkt6.cc pkt6_unittest.cc -libdhcp___unittests_SOURCES += ../pkt4.h ../pkt4.cc pkt4_unittest.cc +libdhcp___unittests_SOURCES += iface_mgr_unittest.cc +libdhcp___unittests_SOURCES += option6_iaaddr_unittest.cc +libdhcp___unittests_SOURCES += option6_ia_unittest.cc +libdhcp___unittests_SOURCES += option6_addrlst_unittest.cc +libdhcp___unittests_SOURCES += option4_addrlst_unittest.cc +libdhcp___unittests_SOURCES += option_unittest.cc +libdhcp___unittests_SOURCES += pkt6_unittest.cc +libdhcp___unittests_SOURCES += pkt4_unittest.cc libdhcp___unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES) $(LOG4CPLUS_INCLUDES) libdhcp___unittests_LDFLAGS = $(AM_LDFLAGS) $(GTEST_LDFLAGS) @@ -51,11 +47,12 @@ if USE_CLANGPP # Boost headers. libdhcp___unittests_CXXFLAGS += -Wno-unused-variable -Wno-unused-parameter endif -libdhcp___unittests_LDADD = $(GTEST_LDADD) +libdhcp___unittests_LDADD = $(top_builddir)/src/lib/dhcp/libb10-dhcp++.la libdhcp___unittests_LDADD += $(top_builddir)/src/lib/log/libb10-log.la libdhcp___unittests_LDADD += $(top_builddir)/src/lib/util/libb10-util.la libdhcp___unittests_LDADD += $(top_builddir)/src/lib/asiolink/libb10-asiolink.la libdhcp___unittests_LDADD += $(top_builddir)/src/lib/exceptions/libb10-exceptions.la +libdhcp___unittests_LDADD += $(GTEST_LDADD) endif noinst_PROGRAMS = $(TESTS) From 576ea605a69bf7edb0f4b1ba10c2db15862c9070 Mon Sep 17 00:00:00 2001 From: Mukund Sivaraman Date: Wed, 19 Sep 2012 15:49:42 +0530 Subject: [PATCH 195/199] [master] Move GTEST_LDADD to the bottom of the list --- tests/tools/perfdhcp/tests/Makefile.am | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/tools/perfdhcp/tests/Makefile.am b/tests/tools/perfdhcp/tests/Makefile.am index 73ec6babc5..54602aff4b 100644 --- a/tests/tools/perfdhcp/tests/Makefile.am +++ b/tests/tools/perfdhcp/tests/Makefile.am @@ -39,12 +39,12 @@ if USE_CLANGPP run_unittests_CXXFLAGS = -Wno-unused-parameter endif -run_unittests_LDADD = $(GTEST_LDADD) -run_unittests_LDADD += $(top_builddir)/src/lib/util/libb10-util.la +run_unittests_LDADD = $(top_builddir)/src/lib/util/libb10-util.la run_unittests_LDADD += $(top_builddir)/src/lib/exceptions/libb10-exceptions.la run_unittests_LDADD += $(top_builddir)/src/lib/asiolink/libb10-asiolink.la run_unittests_LDADD += $(top_builddir)/src/lib/dhcp/libb10-dhcp++.la run_unittests_LDADD += $(top_builddir)/src/lib/util/unittests/libutil_unittests.la +run_unittests_LDADD += $(GTEST_LDADD) endif noinst_PROGRAMS = $(TESTS) From febd7504e90b57453ca53ef754093adf1d7a8899 Mon Sep 17 00:00:00 2001 From: Mukund Sivaraman Date: Wed, 19 Sep 2012 15:53:12 +0530 Subject: [PATCH 196/199] [master] Track origin of uninitalized memory on all Valgrind targets --- Makefile.am | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile.am b/Makefile.am index 0fbb78244f..1ed0d6325f 100644 --- a/Makefile.am +++ b/Makefile.am @@ -30,7 +30,7 @@ endif check-valgrind-suppress: if HAVE_VALGRIND - @VALGRIND_COMMAND="$(VALGRIND) -q --gen-suppressions=all --error-exitcode=1 --suppressions=$(abs_top_srcdir)/src/valgrind-suppressions --suppressions=$(abs_top_srcdir)/src/valgrind-suppressions.revisit --num-callers=48 --leak-check=full --fullpath-after=" \ + @VALGRIND_COMMAND="$(VALGRIND) -q --gen-suppressions=all --track-origins=yes --error-exitcode=1 --suppressions=$(abs_top_srcdir)/src/valgrind-suppressions --suppressions=$(abs_top_srcdir)/src/valgrind-suppressions.revisit --num-callers=48 --leak-check=full --fullpath-after=" \ make -C $(abs_top_builddir) check else @echo "*** Valgrind is required for check-valgrind-suppress ***"; exit 1; From 0187e5f3edb50f42f4f3388decd555518df71bf3 Mon Sep 17 00:00:00 2001 From: Mukund Sivaraman Date: Wed, 19 Sep 2012 15:57:22 +0530 Subject: [PATCH 197/199] [master] Update various .gitignore files --- src/bin/dhcp4/.gitignore | 4 +++- src/bin/dhcp6/.gitignore | 15 +++++---------- src/bin/dhcp6/tests/.gitignore | 1 + tests/tools/perfdhcp/.gitignore | 1 + 4 files changed, 10 insertions(+), 11 deletions(-) create mode 100644 src/bin/dhcp6/tests/.gitignore diff --git a/src/bin/dhcp4/.gitignore b/src/bin/dhcp4/.gitignore index 952db06e0e..86965b9f56 100644 --- a/src/bin/dhcp4/.gitignore +++ b/src/bin/dhcp4/.gitignore @@ -1,4 +1,6 @@ /b10-dhcp4 +/b10-dhcp4.8 +/dhcp4_messages.cc +/dhcp4_messages.h /spec_config.h /spec_config.h.pre -/b10-dhcp4.8 diff --git a/src/bin/dhcp6/.gitignore b/src/bin/dhcp6/.gitignore index eedbb846cb..58781893aa 100644 --- a/src/bin/dhcp6/.gitignore +++ b/src/bin/dhcp6/.gitignore @@ -1,11 +1,6 @@ -*~ -Makefile -Makefile.in -*.o -.deps -.libs -b10-dhcp6 -spec_config.h -spec_config.h.pre -tests/dhcp6_unittests +/b10-dhcp6 /b10-dhcp6.8 +/dhcp6_messages.cc +/dhcp6_messages.h +/spec_config.h +/spec_config.h.pre diff --git a/src/bin/dhcp6/tests/.gitignore b/src/bin/dhcp6/tests/.gitignore new file mode 100644 index 0000000000..e170d18915 --- /dev/null +++ b/src/bin/dhcp6/tests/.gitignore @@ -0,0 +1 @@ +/dhcp6_unittests diff --git a/tests/tools/perfdhcp/.gitignore b/tests/tools/perfdhcp/.gitignore index 1a8375a158..366076699c 100644 --- a/tests/tools/perfdhcp/.gitignore +++ b/tests/tools/perfdhcp/.gitignore @@ -1 +1,2 @@ /perfdhcp +/perfdhcp2 From 704ca46ac3aa9c93c9964eb3495bacd4050f042e Mon Sep 17 00:00:00 2001 From: Tomek Mrugalski Date: Fri, 21 Sep 2012 18:45:21 +0200 Subject: [PATCH 198/199] [2238] Changes after review. --- ChangeLog | 9 ++-- src/lib/asiolink/io_address.h | 23 ++++----- src/lib/asiolink/tests/io_address_unittest.cc | 2 +- src/lib/dhcp/addr_utilities.cc | 50 ++++++++++++++++--- src/lib/dhcp/addr_utilities.h | 12 +++-- src/lib/dhcp/cfgmgr.cc | 26 +++++++--- src/lib/dhcp/cfgmgr.h | 49 ++++++++++++++---- src/lib/dhcp/tests/Makefile.am | 2 + src/lib/dhcp/tests/addr_utilities_unittest.cc | 8 +++ src/lib/dhcp/tests/cfgmgr_unittest.cc | 49 ++++++++++++------ src/lib/dhcp/tests/run_unittests.cc | 2 + 11 files changed, 170 insertions(+), 62 deletions(-) diff --git a/ChangeLog b/ChangeLog index 89011a0b7a..7f8ae861b8 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,9 +1,8 @@ 4XX. [func] tomek - A new library (libb10-dhcpsrv) has been create. Currently its - functionality is limited to a Configuration Manager. CfgMgr - currently only supports basic configuration storage for DHCPv6 - server, but that capability is expected to be expanded in a near - future. + A new library (libb10-dhcpsrv) has been created. At present, it + only holds the code for the DHCP Configuration Manager. Currently + this object only supports basic configuration storage for the DHCPv6 + server, but that capability will be expanded. (Trac #2238, git TBD) 475. [func] naokikambe diff --git a/src/lib/asiolink/io_address.h b/src/lib/asiolink/io_address.h index 2e09260066..a3bb61ac29 100644 --- a/src/lib/asiolink/io_address.h +++ b/src/lib/asiolink/io_address.h @@ -150,18 +150,15 @@ public: /// Operations within one protocol family are obvious. /// Comparisons between v4 and v6 will allways return v4 /// being smaller. This follows boost::asio::ip implementation - bool smallerThan(const IOAddress& other) const { - if (this->getFamily() < other.getFamily()) { - return (true); - } - if (this->getFamily() > other.getFamily()) { - return (false); - } - if (this->getFamily() == AF_INET6) { - return (this->asio_address_.to_v6() < other.asio_address_.to_v6()); - } else { - return (this->asio_address_.to_v4() < other.asio_address_.to_v4()); + bool lessThan(const IOAddress& other) const { + if (this->getFamily() == other.getFamily()) { + if (this->getFamily() == AF_INET6) { + return (this->asio_address_.to_v6() < other.asio_address_.to_v6()); + } else { + return (this->asio_address_.to_v4() < other.asio_address_.to_v4()); + } } + return (this->getFamily() < other.getFamily()); } /// \brief Checks if one address is smaller or equal than the other @@ -173,7 +170,7 @@ public: if (equals(other)) { return (true); } - return (smallerThan(other)); + return (lessThan(other)); } /// \brief Checks if one address is smaller than the other @@ -182,7 +179,7 @@ public: /// /// See \ref smaller_than method for details. bool operator<(const IOAddress& other) const { - return (smallerThan(other)); + return (lessThan(other)); } /// \brief Checks if one address is smaller or equal than the other diff --git a/src/lib/asiolink/tests/io_address_unittest.cc b/src/lib/asiolink/tests/io_address_unittest.cc index 2530ca5e87..a1a90760eb 100644 --- a/src/lib/asiolink/tests/io_address_unittest.cc +++ b/src/lib/asiolink/tests/io_address_unittest.cc @@ -100,7 +100,7 @@ TEST(IOAddressTest, uint32) { EXPECT_EQ(addr3.toText(), "192.0.2.5"); } -TEST(IOAddressTest, compare) { +TEST(IOAddressTest, lessThanEqual) { IOAddress addr1("192.0.2.5"); IOAddress addr2("192.0.2.6"); IOAddress addr3("0.0.0.0"); diff --git a/src/lib/dhcp/addr_utilities.cc b/src/lib/dhcp/addr_utilities.cc index ddd761a00a..15999d496c 100644 --- a/src/lib/dhcp/addr_utilities.cc +++ b/src/lib/dhcp/addr_utilities.cc @@ -14,46 +14,80 @@ #include +using namespace isc::asiolink; + namespace isc { namespace dhcp { isc::asiolink::IOAddress firstAddrInPrefix(const isc::asiolink::IOAddress& prefix, - uint8_t len) { + uint8_t len) { static char bitMask[]= { 0, 0x80, 0xc0, 0xe0, 0xf0, 0xf8, 0xfc, 0xfe, 0xff }; - uint8_t packed[16]; + uint8_t packed[V6ADDRESS_LEN]; + // First we copy the whole address as 16 bytes. memcpy(packed, prefix.getAddress().to_v6().to_bytes().data(), 16); + // If the length is divisible by 8, it is simple. We just zero out the host + // part. Otherwise we need to handle the byte that has to be partially + // zeroed. if (len % 8 != 0) { + + // Get the appropriate mask. It has relevant bits (those that should + // stay) set and irrelevant (those that should be wiped) cleared. uint8_t mask = bitMask[len % 8]; + + // Let's leave only whatever the mask says should not be cleared. packed[len / 8] = packed[len / 8] & mask; - len = (len/8 + 1) * 8; + + // Since we have just dealt with this byte, let's move the prefix length + // to the beginning of the next byte (len is expressed in bits). + len = (len / 8 + 1) * 8; } - for (int i = len / 8; i < 16; ++i) { + + // Clear out the remaining bits. + for (int i = len / 8; i < sizeof(packed); ++i) { packed[i] = 0x0; } + // Finally, let's wrap this into nice and easy IOAddress object. return (isc::asiolink::IOAddress::from_bytes(AF_INET6, packed)); } isc::asiolink::IOAddress lastAddrInPrefix(const isc::asiolink::IOAddress& prefix, - uint8_t len) { + uint8_t len) { static char bitMask[]= { 0, 0x80, 0xc0, 0xe0, 0xf0, 0xf8, 0xfc, 0xfe, 0xff }; - uint8_t packed[16]; + uint8_t packed[V6ADDRESS_LEN]; + // First we copy the whole address as 16 bytes. memcpy(packed, prefix.getAddress().to_v6().to_bytes().data(), 16); + // if the length is divisible by 8, it is simple. We just fill the host part + // with ones. Otherwise we need to handle the byte that has to be partially + // zeroed. if (len % 8 != 0) { + // Get the appropriate mask. It has relevant bits (those that should + // stay) set and irrelevant (those that should be set to 1) cleared. uint8_t mask = bitMask[len % 8]; + + // Let's set those irrelevant bits with 1. It would be perhaps + // easier to not use negation here and invert bitMask content. However, + // with this approach, we can use the same mask in first and last + // address calculations. packed[len / 8] = packed[len / 8] | ~mask; - len = (len/8 + 1) * 8; + + // Since we have just dealt with this byte, let's move the prefix length + // to the beginning of the next byte (len is expressed in bits). + len = (len / 8 + 1) * 8; } - for (int i = len / 8; i < 16; ++i) { + + // Finally set remaining bits to 1. + for (int i = len / 8; i < sizeof(packed); ++i) { packed[i] = 0xff; } + // Finally, let's wrap this into nice and easy IOAddress object. return (isc::asiolink::IOAddress::from_bytes(AF_INET6, packed)); } diff --git a/src/lib/dhcp/addr_utilities.h b/src/lib/dhcp/addr_utilities.h index d82a8a693d..15532d0543 100644 --- a/src/lib/dhcp/addr_utilities.h +++ b/src/lib/dhcp/addr_utilities.h @@ -18,32 +18,36 @@ namespace isc { namespace dhcp { /// This code is based on similar code from the Dibbler project. I, Tomasz Mrugalski, -/// as a sole creater of that code hereby release it under BSD license for the benefit +/// as a sole creator of that code hereby release it under BSD license for the benefit /// of the BIND10 project. /// @brief returns a first address in a given prefix /// /// Example: For 2001:db8:1::deaf:beef and length /120 the function will return -/// 2001:db8:1::dead:bee0. See also @ref lastAddrInPrefix. +/// 2001:db8:1::dead:be00. See also @ref lastAddrInPrefix. +/// +/// @todo It currently works for v6 only and will throw if v4 address is passed. /// /// @param prefix and address that belongs to a prefix /// @param len prefix length /// /// @return first address from a prefix isc::asiolink::IOAddress firstAddrInPrefix(const isc::asiolink::IOAddress& prefix, - uint8_t len); + uint8_t len); /// @brief returns a last address in a given prefix /// /// Example: For 2001:db8:1::deaf:beef and length /112 the function will return /// 2001:db8:1::dead:ffff. See also @ref firstAddrInPrefix. /// +/// @todo It currently works for v6 only and will throw if v4 address is passed. +/// /// @param prefix and address that belongs to a prefix /// @param len prefix length /// /// @return first address from a prefix isc::asiolink::IOAddress lastAddrInPrefix(const isc::asiolink::IOAddress& prefix, - uint8_t len); + uint8_t len); }; }; diff --git a/src/lib/dhcp/cfgmgr.cc b/src/lib/dhcp/cfgmgr.cc index 1bb76c8085..ff55a90ccf 100644 --- a/src/lib/dhcp/cfgmgr.cc +++ b/src/lib/dhcp/cfgmgr.cc @@ -27,8 +27,8 @@ Pool::Pool(const isc::asiolink::IOAddress& first, :id_(getNextID()), first_(first), last_(last) { } -bool Pool::inRange(const isc::asiolink::IOAddress& addr) { - return ( first_.smallerEqual(addr) && addr.smallerEqual(last_) ); +bool Pool::inRange(const isc::asiolink::IOAddress& addr) const { + return (first_.smallerEqual(addr) && addr.smallerEqual(last_)); } Pool6::Pool6(Pool6Type type, const isc::asiolink::IOAddress& first, @@ -46,12 +46,14 @@ Pool6::Pool6(Pool6Type type, const isc::asiolink::IOAddress& first, // we need to comment it and uncomment lines below. // On one hand, letting the user specify 2001::f - 2001::1 is nice, but // on the other hand, 2001::1 may be a typo and the user really meant - // 2001::1:0 (or 1something), so a at least a warning would be useful. + // 2001::1:0 (or 1 followed by some hex digit), so a at least a warning + // would be useful. // first_ = last; // last_ = first; } + // TYPE_PD is not supported by this constructor. first-last style // parameters are for IA and TA only. There is another dedicated // constructor for that (it uses prefix/length) @@ -75,6 +77,9 @@ Pool6::Pool6(Pool6Type type, const isc::asiolink::IOAddress& prefix, isc_throw(BadValue, "Invalid prefix length"); } + /// @todo: We should probably implement checks against weird addresses + /// here, like ::, starting with fe80, starting with ff etc. . + // Let's now calculate the last address in defined pool last_ = lastAddrInPrefix(prefix, prefix_len); } @@ -91,11 +96,11 @@ Subnet::Subnet(const isc::asiolink::IOAddress& prefix, uint8_t len, } } -bool Subnet::inRange(const isc::asiolink::IOAddress& addr) { +bool Subnet::inRange(const isc::asiolink::IOAddress& addr) const { IOAddress first = firstAddrInPrefix(prefix_, prefix_len_); IOAddress last = lastAddrInPrefix(prefix_, prefix_len_); - return ( (first <= addr) && (addr <= last) ); + return ((first <= addr) && (addr <= last)); } Subnet6::Subnet6(const isc::asiolink::IOAddress& prefix, uint8_t length, @@ -106,7 +111,7 @@ Subnet6::Subnet6(const isc::asiolink::IOAddress& prefix, uint8_t length, :Subnet(prefix, length, t1, t2, valid_lifetime), preferred_(preferred_lifetime){ if (prefix.getFamily() != AF_INET6) { - isc_throw(BadValue, "Invalid prefix " << prefix.toText() + isc_throw(BadValue, "Non IPv6 prefix " << prefix.toText() << " specified in subnet6"); } } @@ -121,6 +126,8 @@ void Subnet6::addPool6(const Pool6Ptr& pool) { << ") subnet6"); } + /// @todo: Check that pools do not overlap + pools_.push_back(pool); } @@ -153,6 +160,13 @@ Subnet6Ptr CfgMgr::getSubnet6(const isc::asiolink::IOAddress& hint) { // If there's only one subnet configured, let's just use it + // The idea is to keep small deployments easy. In a small network - one + // router that also runs DHCPv6 server. Users specifies a single pool and + // expects it to just work. Without this, the server would complain that it + // doesn't have IP address on its interfaces that matches that + // configuration. Such requirement makes sense in IPv4, but not in IPv6. + // The server does not need to have a global address (using just link-local + // is ok for DHCPv6 server) from the pool it serves. if (subnets6_.size() == 1) { return (subnets6_[0]); } diff --git a/src/lib/dhcp/cfgmgr.h b/src/lib/dhcp/cfgmgr.h index f3ffe98b27..48cb847fe2 100644 --- a/src/lib/dhcp/cfgmgr.h +++ b/src/lib/dhcp/cfgmgr.h @@ -31,7 +31,7 @@ class Pool6; class Subnet6; -/// @brief this template specifes a parameter value +/// @brief this template specifies a parameter value /// /// This template class is used to store configuration parameters, like lifetime or T1. /// It defines 3 parameters: min, default, and max value. There are 2 constructors: @@ -48,15 +48,18 @@ public: /// /// Typically: uint32_t to Triplet assignment. It is very convenient /// to be able to simply write Triplet x = 7; - Triplet& operator = (T base_type) { - return Triplet(base_type); + Triplet operator=(T other) { + min_ = other; + default_ = other; + max_ = other; + return *this; } /// @brief triplet to base type conversion /// /// Typically: Triplet to uint32_t assignment. It is very convenient /// to be able to simply write uint32_t z = x; (where x is a Triplet) - operator T () const { + operator T() const { return (default_); } @@ -73,7 +76,7 @@ public: /// @throw BadValue if min <= def <= max rule is violated Triplet(T min, T def, T max) :min_(min), default_(def), max_(max) { - if ( (min_>def) || (def > max_) ) { + if ( (min_ > def) || (def > max_) ) { isc_throw(BadValue, "Invalid triplet values."); } } @@ -127,6 +130,7 @@ public: /// @brief returns Pool-id /// + /// @return pool-id value /// Pool-id is an unique value that can be used to identify a pool. uint32_t getId() const { return (id_); @@ -148,7 +152,7 @@ public: /// @brief Checks if a given address is in the range. /// /// @return true, if the address is in pool - bool inRange(const isc::asiolink::IOAddress& addr); + bool inRange(const isc::asiolink::IOAddress& addr) const; protected: @@ -170,7 +174,7 @@ protected: /// @brief pool-id /// - /// This ID is used to indentify this specific pool. + /// This ID is used to identify this specific pool. uint32_t id_; /// @brief The first address in a pool @@ -227,7 +231,7 @@ public: return (type_); } -protected: +private: /// @brief defines a pool type Pool6Type type_; @@ -255,7 +259,7 @@ typedef std::vector Pool6Collection; class Subnet { public: /// @brief checks if specified address is in range - bool inRange(const isc::asiolink::IOAddress& addr); + bool inRange(const isc::asiolink::IOAddress& addr) const; /// @brief return valid-lifetime for addresses in that prefix Triplet getValid() const { @@ -317,29 +321,54 @@ protected: /// This class represents an IPv6 subnet. class Subnet6 : public Subnet { public: + + /// @brief Constructor with all parameters + /// + /// @param prefix Subnet6 prefix + /// @param length prefix length + /// @param t1 renewal timer (in seconds) + /// @param t2 rebind timer (in seconds) + /// @param preferred_lifetime preferred lifetime of leases (in seconds) + /// @param valid_lifetime preferred lifetime of leases (in seconds) Subnet6(const isc::asiolink::IOAddress& prefix, uint8_t length, const Triplet& t1, const Triplet& t2, const Triplet& preferred_lifetime, const Triplet& valid_lifetime); + /// @brief Returns preverred lifetime (in seconds) + /// + /// @return a triplet with preferred lifetime Triplet getPreferred() const { return (preferred_); } + /// @brief Returns a pool that specified address belongs to + /// + /// @param hint address that the returned pool should cover (optional) + /// @return Pointer to found pool6 (or NULL) Pool6Ptr getPool6(const isc::asiolink::IOAddress& hint = isc::asiolink::IOAddress("::")); + /// @brief Adds a new pool. + /// @param pool pool to be added void addPool6(const Pool6Ptr& pool); + /// @brief returns all pools + /// + /// The reference is only valid as long as the object that + /// returned it. + /// + /// @return a collection of all pools const Pool6Collection& getPools() const { return pools_; } protected: - /// collection of pools in that list + /// @brief collection of pools in that list Pool6Collection pools_; + /// @brief a triplet with preferred lifetime (in seconds) Triplet preferred_; }; diff --git a/src/lib/dhcp/tests/Makefile.am b/src/lib/dhcp/tests/Makefile.am index 42abcb65bc..70c87c8c77 100644 --- a/src/lib/dhcp/tests/Makefile.am +++ b/src/lib/dhcp/tests/Makefile.am @@ -51,6 +51,7 @@ libdhcpsrv_unittests_LDADD = $(GTEST_LDADD) libdhcpsrv_unittests_LDADD += $(top_builddir)/src/lib/exceptions/libb10-exceptions.la libdhcpsrv_unittests_LDADD += $(top_builddir)/src/lib/asiolink/libb10-asiolink.la libdhcpsrv_unittests_LDADD += $(top_builddir)/src/lib/dhcp/libb10-dhcpsrv.la +libdhcpsrv_unittests_LDADD += $(top_builddir)/src/lib/log/libb10-log.la if USE_CLANGPP @@ -66,6 +67,7 @@ libdhcp___unittests_LDADD += $(top_builddir)/src/lib/log/libb10-log.la libdhcp___unittests_LDADD += $(top_builddir)/src/lib/util/libb10-util.la libdhcp___unittests_LDADD += $(top_builddir)/src/lib/asiolink/libb10-asiolink.la libdhcp___unittests_LDADD += $(top_builddir)/src/lib/exceptions/libb10-exceptions.la +libdhcp___unittests_LDADD += $(top_builddir)/src/lib/log/libb10-log.la libdhcp___unittests_LDADD += $(GTEST_LDADD) endif diff --git a/src/lib/dhcp/tests/addr_utilities_unittest.cc b/src/lib/dhcp/tests/addr_utilities_unittest.cc index c9daa9ba49..46207b29db 100644 --- a/src/lib/dhcp/tests/addr_utilities_unittest.cc +++ b/src/lib/dhcp/tests/addr_utilities_unittest.cc @@ -53,6 +53,14 @@ TEST(Pool6Test, lastAddrInPrefix) { EXPECT_EQ("2001::3f", lastAddrInPrefix(addr2, 122).toText()); EXPECT_EQ("2001::7f", lastAddrInPrefix(addr2, 121).toText()); EXPECT_EQ("2001::ff", lastAddrInPrefix(addr2, 120).toText()); + + // Let's check extreme cases + IOAddress anyAddr("::"); + EXPECT_EQ("7fff:ffff:ffff:ffff:ffff:ffff:ffff:ffff", + lastAddrInPrefix(anyAddr, 1).toText()); + EXPECT_EQ("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff", + lastAddrInPrefix(anyAddr, 0).toText()); + EXPECT_EQ("::", lastAddrInPrefix(anyAddr, 128).toText()); } TEST(Pool6Test, firstAddrInPrefix) { diff --git a/src/lib/dhcp/tests/cfgmgr_unittest.cc b/src/lib/dhcp/tests/cfgmgr_unittest.cc index 6592803c3b..7f287eaf80 100644 --- a/src/lib/dhcp/tests/cfgmgr_unittest.cc +++ b/src/lib/dhcp/tests/cfgmgr_unittest.cc @@ -35,28 +35,28 @@ namespace { // constructor validation TEST(TripletTest, constructor) { - uint32_t min = 10; - uint32_t value = 20; - uint32_t max = 30; + const uint32_t min = 10; + const uint32_t value = 20; + const uint32_t max = 30; Triplet x(min, value, max); - EXPECT_EQ(10, x.getMin()); - EXPECT_EQ(20, x.get()); - EXPECT_EQ(30, x.getMax()); + EXPECT_EQ(min, x.getMin()); + EXPECT_EQ(value, x.get()); + EXPECT_EQ(max, x.getMax()); // requested values below min should return allowed min value - EXPECT_EQ(10, x.get(5)); + EXPECT_EQ(min, x.get(min - 5)); - EXPECT_EQ(10, x.get(10)); + EXPECT_EQ(min, x.get(min)); // requesting a value from within the range (min < x < max) should // return the requested value EXPECT_EQ(17, x.get(17)); - EXPECT_EQ(30, x.get(30)); + EXPECT_EQ(max, x.get(max)); - EXPECT_EQ(30, x.get(35)); + EXPECT_EQ(max, x.get(max + 5)); // this will be boring. It is expected to return 42 no matter what Triplet y(42); @@ -77,15 +77,26 @@ TEST(TripletTest, operator) { uint32_t x = 47; - // assignment operator: uint32_t => triplet - Triplet y = x; + Triplet foo(1,2,3); + Triplet bar(4,5,6); - EXPECT_EQ(47, y.get()); + foo = bar; + + EXPECT_EQ(4, foo.getMin()); + EXPECT_EQ(5, foo.get()); + EXPECT_EQ(6, foo.getMax()); + + // assignment operator: uint32_t => triplet + Triplet y(0); + y = x; + + EXPECT_EQ(x, y.get()); // let's try the other way around: triplet => uint32_t - uint32_t z = y; + uint32_t z = 0; + z = y; - EXPECT_EQ(47, z); + EXPECT_EQ(x, z); } // check if specified values are sane @@ -131,6 +142,14 @@ TEST(Pool6Test, constructor_prefix_len) { EXPECT_EQ("2001:db8:1::", pool1.getFirstAddress().toText()); EXPECT_EQ("2001:db8:1::ffff:ffff", pool1.getLastAddress().toText()); + // No such thing as /130 prefix + EXPECT_THROW(Pool6(Pool6::TYPE_IA, IOAddress("2001:db8::"), 130), + BadValue); + + // /0 prefix does not make sense + EXPECT_THROW(Pool6(Pool6::TYPE_IA, IOAddress("2001:db8::"), 0), + BadValue); + // This is Pool6, IPv4 addresses do not belong here EXPECT_THROW(Pool6(Pool6::TYPE_IA, IOAddress("192.168.0.2"), 96), BadValue); diff --git a/src/lib/dhcp/tests/run_unittests.cc b/src/lib/dhcp/tests/run_unittests.cc index d3dd12e867..0126645f62 100644 --- a/src/lib/dhcp/tests/run_unittests.cc +++ b/src/lib/dhcp/tests/run_unittests.cc @@ -13,10 +13,12 @@ // PERFORMANCE OF THIS SOFTWARE. #include +#include int main(int argc, char* argv[]) { ::testing::InitGoogleTest(&argc, argv); + isc::log::initLogger(); int result = RUN_ALL_TESTS(); From 3690baaf0660af752b00a370fadcd60859c50ca4 Mon Sep 17 00:00:00 2001 From: Tomek Mrugalski Date: Fri, 21 Sep 2012 19:01:14 +0200 Subject: [PATCH 199/199] [2238] Pool, Subnet and Triplet moved to separate files. --- src/lib/dhcp/Makefile.am | 3 + src/lib/dhcp/cfgmgr.cc | 126 -------------- src/lib/dhcp/cfgmgr.h | 352 +-------------------------------------- src/lib/dhcp/pool.cc | 87 ++++++++++ src/lib/dhcp/pool.h | 155 +++++++++++++++++ src/lib/dhcp/subnet.cc | 91 ++++++++++ src/lib/dhcp/subnet.h | 161 ++++++++++++++++++ src/lib/dhcp/triplet.h | 108 ++++++++++++ 8 files changed, 607 insertions(+), 476 deletions(-) create mode 100644 src/lib/dhcp/pool.cc create mode 100644 src/lib/dhcp/pool.h create mode 100644 src/lib/dhcp/subnet.cc create mode 100644 src/lib/dhcp/subnet.h create mode 100644 src/lib/dhcp/triplet.h diff --git a/src/lib/dhcp/Makefile.am b/src/lib/dhcp/Makefile.am index a45884858b..4c22b3596c 100644 --- a/src/lib/dhcp/Makefile.am +++ b/src/lib/dhcp/Makefile.am @@ -30,6 +30,9 @@ libb10_dhcp___la_SOURCES += pkt6.cc pkt6.h libb10_dhcp___la_SOURCES += pkt4.cc pkt4.h libb10_dhcpsrv_la_SOURCES = cfgmgr.cc cfgmgr.h +libb10_dhcpsrv_la_SOURCES += pool.cc pool.h +libb10_dhcpsrv_la_SOURCES += subnet.cc subnet.h +libb10_dhcpsrv_la_SOURCES += triplet.h libb10_dhcpsrv_la_SOURCES += addr_utilities.cc addr_utilities.h libb10_dhcpsrv_la_CXXFLAGS = $(AM_CXXFLAGS) libb10_dhcpsrv_la_CPPFLAGS = $(AM_CPPFLAGS) $(LOG4CPLUS_INCLUDES) diff --git a/src/lib/dhcp/cfgmgr.cc b/src/lib/dhcp/cfgmgr.cc index ff55a90ccf..15e3ad97d6 100644 --- a/src/lib/dhcp/cfgmgr.cc +++ b/src/lib/dhcp/cfgmgr.cc @@ -12,7 +12,6 @@ // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR // PERFORMANCE OF THIS SOFTWARE. -#include #include #include @@ -22,132 +21,7 @@ using namespace isc::util; namespace isc { namespace dhcp { -Pool::Pool(const isc::asiolink::IOAddress& first, - const isc::asiolink::IOAddress& last) - :id_(getNextID()), first_(first), last_(last) { -} -bool Pool::inRange(const isc::asiolink::IOAddress& addr) const { - return (first_.smallerEqual(addr) && addr.smallerEqual(last_)); -} - -Pool6::Pool6(Pool6Type type, const isc::asiolink::IOAddress& first, - const isc::asiolink::IOAddress& last) - :Pool(first, last), type_(type), prefix_len_(0) { - - // check if specified address boundaries are sane - if (first.getFamily() != AF_INET6 || last.getFamily() != AF_INET6) { - isc_throw(BadValue, "Invalid Pool6 address boundaries: not IPv6"); - } - - if (last < first) { - isc_throw(BadValue, "Upper boundary is smaller than lower boundary."); - // This check is a bit strict. If we decide that it is too strict, - // we need to comment it and uncomment lines below. - // On one hand, letting the user specify 2001::f - 2001::1 is nice, but - // on the other hand, 2001::1 may be a typo and the user really meant - // 2001::1:0 (or 1 followed by some hex digit), so a at least a warning - // would be useful. - - // first_ = last; - // last_ = first; - } - - - // TYPE_PD is not supported by this constructor. first-last style - // parameters are for IA and TA only. There is another dedicated - // constructor for that (it uses prefix/length) - if ((type != TYPE_IA) && (type != TYPE_TA)) { - isc_throw(BadValue, "Invalid Pool6 type specified"); - } -} - -Pool6::Pool6(Pool6Type type, const isc::asiolink::IOAddress& prefix, - uint8_t prefix_len) - :Pool(prefix, IOAddress("::")), - type_(type), prefix_len_(prefix_len) { - - // check if the prefix is sane - if (prefix.getFamily() != AF_INET6) { - isc_throw(BadValue, "Invalid Pool6 address boundaries: not IPv6"); - } - - // check if the prefix length is sane - if (prefix_len == 0 || prefix_len > 128) { - isc_throw(BadValue, "Invalid prefix length"); - } - - /// @todo: We should probably implement checks against weird addresses - /// here, like ::, starting with fe80, starting with ff etc. . - - // Let's now calculate the last address in defined pool - last_ = lastAddrInPrefix(prefix, prefix_len); -} - -Subnet::Subnet(const isc::asiolink::IOAddress& prefix, uint8_t len, - const Triplet& t1, - const Triplet& t2, - const Triplet& valid_lifetime) - :id_(getNextID()), prefix_(prefix), prefix_len_(len), t1_(t1), - t2_(t2), valid_(valid_lifetime) { - if ( (prefix.getFamily() == AF_INET6 && len > 128) || - (prefix.getFamily() == AF_INET && len > 32) ) { - isc_throw(BadValue, "Invalid prefix length specified for subnet: " << len); - } -} - -bool Subnet::inRange(const isc::asiolink::IOAddress& addr) const { - IOAddress first = firstAddrInPrefix(prefix_, prefix_len_); - IOAddress last = lastAddrInPrefix(prefix_, prefix_len_); - - return ((first <= addr) && (addr <= last)); -} - -Subnet6::Subnet6(const isc::asiolink::IOAddress& prefix, uint8_t length, - const Triplet& t1, - const Triplet& t2, - const Triplet& preferred_lifetime, - const Triplet& valid_lifetime) - :Subnet(prefix, length, t1, t2, valid_lifetime), - preferred_(preferred_lifetime){ - if (prefix.getFamily() != AF_INET6) { - isc_throw(BadValue, "Non IPv6 prefix " << prefix.toText() - << " specified in subnet6"); - } -} - -void Subnet6::addPool6(const Pool6Ptr& pool) { - IOAddress first_addr = pool->getFirstAddress(); - IOAddress last_addr = pool->getLastAddress(); - - if (!inRange(first_addr) || !inRange(last_addr)) { - isc_throw(BadValue, "Pool6 (" << first_addr.toText() << "-" << last_addr.toText() - << " does not belong in this (" << prefix_ << "/" << prefix_len_ - << ") subnet6"); - } - - /// @todo: Check that pools do not overlap - - pools_.push_back(pool); -} - -Pool6Ptr Subnet6::getPool6(const isc::asiolink::IOAddress& hint /* = IOAddress("::")*/ ) { - Pool6Ptr candidate; - for (Pool6Collection::iterator pool = pools_.begin(); pool != pools_.end(); ++pool) { - - // if we won't find anything better, then let's just use the first pool - if (!candidate) { - candidate = *pool; - } - - // if the client provided a pool and there's a pool that hint is valid in, - // then let's use that pool - if ((*pool)->inRange(hint)) { - return (*pool); - } - } - return (candidate); -} CfgMgr& diff --git a/src/lib/dhcp/cfgmgr.h b/src/lib/dhcp/cfgmgr.h index 48cb847fe2..5b73f2b966 100644 --- a/src/lib/dhcp/cfgmgr.h +++ b/src/lib/dhcp/cfgmgr.h @@ -23,360 +23,12 @@ #include #include #include +#include +#include namespace isc { namespace dhcp { -class Pool6; - -class Subnet6; - -/// @brief this template specifies a parameter value -/// -/// This template class is used to store configuration parameters, like lifetime or T1. -/// It defines 3 parameters: min, default, and max value. There are 2 constructors: -/// - simple (just one value that sets all parameters) -/// - extended (that sets default value and two thresholds) -/// It will be used with integer types. It provides necessary operators, so -/// it can be assigned to a plain integer or integer assigned to a Triplet. -/// See TripletTest.operator test for details on an easy Triplet usage. -template -class Triplet { -public: - - /// @brief base type to Triple conversion - /// - /// Typically: uint32_t to Triplet assignment. It is very convenient - /// to be able to simply write Triplet x = 7; - Triplet operator=(T other) { - min_ = other; - default_ = other; - max_ = other; - return *this; - } - - /// @brief triplet to base type conversion - /// - /// Typically: Triplet to uint32_t assignment. It is very convenient - /// to be able to simply write uint32_t z = x; (where x is a Triplet) - operator T() const { - return (default_); - } - - /// @brief sets a fixed value - /// - /// This constructor assigns a fixed (i.e. no range, just a single value) - /// value. - Triplet(T value) - :min_(value), default_(value), max_(value) { - } - - /// @brief sets the default value and thresholds - /// - /// @throw BadValue if min <= def <= max rule is violated - Triplet(T min, T def, T max) - :min_(min), default_(def), max_(max) { - if ( (min_ > def) || (def > max_) ) { - isc_throw(BadValue, "Invalid triplet values."); - } - } - - /// @brief returns a minimum allowed value - T getMin() const { return min_;} - - /// @brief returns the default value - T get() const { return default_;} - - /// @brief returns value with a hint - /// - /// DHCP protocol treats any values sent by a client as hints. - /// This is a method that implements that. We can assign any value - /// from configured range that client asks. - T get(T hint) const { - if (hint <= min_) { - return (min_); - } - - if (hint >= max_) { - return (max_); - } - - return (hint); - } - - /// @brief returns a maximum allowed value - T getMax() const { return max_; } - -protected: - - /// @brief the minimum value - T min_; - - /// @brief the default value - T default_; - - /// @brief the maximum value - T max_; -}; - - -/// @brief base class for Pool4 and Pool6 -/// -/// Stores information about pool of IPv4 or IPv6 addresses. -/// That is a basic component of a configuration. -class Pool { - -public: - - /// @brief returns Pool-id - /// - /// @return pool-id value - /// Pool-id is an unique value that can be used to identify a pool. - uint32_t getId() const { - return (id_); - } - - /// @brief Returns the first address in a pool. - /// - /// @return first address in a pool - const isc::asiolink::IOAddress& getFirstAddress() const { - return (first_); - } - - /// @brief Returns the last address in a pool. - /// @return last address in a pool - const isc::asiolink::IOAddress& getLastAddress() const { - return (last_); - } - - /// @brief Checks if a given address is in the range. - /// - /// @return true, if the address is in pool - bool inRange(const isc::asiolink::IOAddress& addr) const; - -protected: - - /// @brief protected constructor - /// - /// This constructor is protected to prevent anyone from instantiating - /// Pool class directly. Instances of Pool4 and Pool6 should be created - /// instead. - Pool(const isc::asiolink::IOAddress& first, - const isc::asiolink::IOAddress& last); - - /// @brief returns the next unique Pool-ID - /// - /// @return the next unique Pool-ID - static uint32_t getNextID() { - static uint32_t id = 0; - return (id++); - } - - /// @brief pool-id - /// - /// This ID is used to identify this specific pool. - uint32_t id_; - - /// @brief The first address in a pool - isc::asiolink::IOAddress first_; - - /// @brief The last address in a pool - isc::asiolink::IOAddress last_; - - /// @brief Comments field - /// - /// @todo: This field is currently not used. - std::string comments_; -}; - -/// @brief Pool information for IPv6 addresses and prefixes -/// -/// It holds information about pool6, i.e. a range of IPv6 address space that -/// is configured for DHCP allocation. -class Pool6 : public Pool { -public: - - /// @brief specifies Pool type - /// - /// Currently there are 3 pool types defined in DHCPv6: - /// - Non-temporary addresses (conveyed in IA_NA) - /// - Temporary addresses (conveyed in IA_TA) - /// - Delegated Prefixes (conveyed in IA_PD) - /// There is a new one being worked on (IA_PA, see draft-ietf-dhc-host-gen-id), but - /// support for it is not planned for now. - typedef enum { - TYPE_IA, - TYPE_TA, - TYPE_PD - } Pool6Type; - - /// @brief the constructor for Pool6 "min-max" style definition - /// - /// @param first the first address in a pool - /// @param last the last address in a pool - Pool6(Pool6Type type, const isc::asiolink::IOAddress& first, - const isc::asiolink::IOAddress& last); - - /// @brief the constructor for Pool6 "prefix/len" style definition - /// - /// @param prefix specifies prefix of the pool - /// @param prefix_len specifies length of the prefix of the pool - Pool6(Pool6Type type, const isc::asiolink::IOAddress& prefix, - uint8_t prefix_len); - - /// @brief returns pool type - /// - /// @return pool type - Pool6Type getType() const { - return (type_); - } - -private: - /// @brief defines a pool type - Pool6Type type_; - - /// @brief prefix length - /// used by TYPE_PD only (zeroed for other types) - uint8_t prefix_len_; -}; - -/// @brief a pointer an IPv6 Pool -typedef boost::shared_ptr Pool6Ptr; - -/// @brief a container for IPv6 Pools -typedef std::vector Pool6Collection; - -/// @brief a base class for Subnet4 and Subnet6 -/// -/// This class presents a common base for IPv4 and IPv6 subnets. -/// In a physical sense, a subnet defines a single network link with all devices -/// attached to it. In most cases all devices attached to a single link can -/// share the same parameters. Therefore Subnet holds several values that are -/// typically shared by all hosts: renew timer (T1), rebind timer (T2) and -/// leased addresses lifetime (valid-lifetime). -/// -/// @todo: Implement support for options here -class Subnet { -public: - /// @brief checks if specified address is in range - bool inRange(const isc::asiolink::IOAddress& addr) const; - - /// @brief return valid-lifetime for addresses in that prefix - Triplet getValid() const { - return (valid_); - } - - /// @brief returns T1 (renew timer), expressed in seconds - Triplet getT1() const { - return (t1_); - } - - /// @brief returns T2 (rebind timer), expressed in seconds - Triplet getT2() const { - return (t2_); - } - -protected: - /// @brief protected constructor - // - /// By making the constructor protected, we make sure that noone will - /// ever instantiate that class. Pool4 and Pool6 should be used instead. - Subnet(const isc::asiolink::IOAddress& prefix, uint8_t len, - const Triplet& t1, - const Triplet& t2, - const Triplet& valid_lifetime); - - /// @brief returns the next unique Subnet-ID - /// - /// @return the next unique Subnet-ID - static uint32_t getNextID() { - static uint32_t id = 0; - return (id++); - } - - /// @brief subnet-id - /// - /// Subnet-id is a unique value that can be used to find or identify - /// a Subnet4 or Subnet6. - uint32_t id_; - - /// @brief a prefix of the subnet - isc::asiolink::IOAddress prefix_; - - /// @brief a prefix length of the subnet - uint8_t prefix_len_; - - /// @brief a tripet (min/default/max) holding allowed renew timer values - Triplet t1_; - - /// @brief a tripet (min/default/max) holding allowed rebind timer values - Triplet t2_; - - /// @brief a tripet (min/default/max) holding allowed valid lifetime values - Triplet valid_; -}; - -/// @brief A configuration holder for IPv6 subnet. -/// -/// This class represents an IPv6 subnet. -class Subnet6 : public Subnet { -public: - - /// @brief Constructor with all parameters - /// - /// @param prefix Subnet6 prefix - /// @param length prefix length - /// @param t1 renewal timer (in seconds) - /// @param t2 rebind timer (in seconds) - /// @param preferred_lifetime preferred lifetime of leases (in seconds) - /// @param valid_lifetime preferred lifetime of leases (in seconds) - Subnet6(const isc::asiolink::IOAddress& prefix, uint8_t length, - const Triplet& t1, - const Triplet& t2, - const Triplet& preferred_lifetime, - const Triplet& valid_lifetime); - - /// @brief Returns preverred lifetime (in seconds) - /// - /// @return a triplet with preferred lifetime - Triplet getPreferred() const { - return (preferred_); - } - - /// @brief Returns a pool that specified address belongs to - /// - /// @param hint address that the returned pool should cover (optional) - /// @return Pointer to found pool6 (or NULL) - Pool6Ptr getPool6(const isc::asiolink::IOAddress& hint = - isc::asiolink::IOAddress("::")); - - /// @brief Adds a new pool. - /// @param pool pool to be added - void addPool6(const Pool6Ptr& pool); - - /// @brief returns all pools - /// - /// The reference is only valid as long as the object that - /// returned it. - /// - /// @return a collection of all pools - const Pool6Collection& getPools() const { - return pools_; - } - -protected: - /// @brief collection of pools in that list - Pool6Collection pools_; - - /// @brief a triplet with preferred lifetime (in seconds) - Triplet preferred_; -}; - -/// @brief A pointer to a Subnet6 object -typedef boost::shared_ptr Subnet6Ptr; - -/// @brief A collection of Subnet6 objects -typedef std::vector Subnet6Collection; /// @brief Configuration Manager /// diff --git a/src/lib/dhcp/pool.cc b/src/lib/dhcp/pool.cc new file mode 100644 index 0000000000..da8a2e3389 --- /dev/null +++ b/src/lib/dhcp/pool.cc @@ -0,0 +1,87 @@ +// Copyright (C) 2012 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 +#include +#include + +using namespace isc::asiolink; + +namespace isc { +namespace dhcp { + +Pool::Pool(const isc::asiolink::IOAddress& first, + const isc::asiolink::IOAddress& last) + :id_(getNextID()), first_(first), last_(last) { +} + +bool Pool::inRange(const isc::asiolink::IOAddress& addr) const { + return (first_.smallerEqual(addr) && addr.smallerEqual(last_)); +} + +Pool6::Pool6(Pool6Type type, const isc::asiolink::IOAddress& first, + const isc::asiolink::IOAddress& last) + :Pool(first, last), type_(type), prefix_len_(0) { + + // check if specified address boundaries are sane + if (first.getFamily() != AF_INET6 || last.getFamily() != AF_INET6) { + isc_throw(BadValue, "Invalid Pool6 address boundaries: not IPv6"); + } + + if (last < first) { + isc_throw(BadValue, "Upper boundary is smaller than lower boundary."); + // This check is a bit strict. If we decide that it is too strict, + // we need to comment it and uncomment lines below. + // On one hand, letting the user specify 2001::f - 2001::1 is nice, but + // on the other hand, 2001::1 may be a typo and the user really meant + // 2001::1:0 (or 1 followed by some hex digit), so a at least a warning + // would be useful. + + // first_ = last; + // last_ = first; + } + + + // TYPE_PD is not supported by this constructor. first-last style + // parameters are for IA and TA only. There is another dedicated + // constructor for that (it uses prefix/length) + if ((type != TYPE_IA) && (type != TYPE_TA)) { + isc_throw(BadValue, "Invalid Pool6 type specified"); + } +} + +Pool6::Pool6(Pool6Type type, const isc::asiolink::IOAddress& prefix, + uint8_t prefix_len) + :Pool(prefix, IOAddress("::")), + type_(type), prefix_len_(prefix_len) { + + // check if the prefix is sane + if (prefix.getFamily() != AF_INET6) { + isc_throw(BadValue, "Invalid Pool6 address boundaries: not IPv6"); + } + + // check if the prefix length is sane + if (prefix_len == 0 || prefix_len > 128) { + isc_throw(BadValue, "Invalid prefix length"); + } + + /// @todo: We should probably implement checks against weird addresses + /// here, like ::, starting with fe80, starting with ff etc. . + + // Let's now calculate the last address in defined pool + last_ = lastAddrInPrefix(prefix, prefix_len); +} + +}; // end of isc::dhcp namespace +}; // end of isc namespace diff --git a/src/lib/dhcp/pool.h b/src/lib/dhcp/pool.h new file mode 100644 index 0000000000..8f6fd8675f --- /dev/null +++ b/src/lib/dhcp/pool.h @@ -0,0 +1,155 @@ +// Copyright (C) 2012 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. + +#ifndef POOL_H +#define POOL_H + +#include +#include +#include + +namespace isc { +namespace dhcp { + +/// @brief base class for Pool4 and Pool6 +/// +/// Stores information about pool of IPv4 or IPv6 addresses. +/// That is a basic component of a configuration. +class Pool { + +public: + + /// @brief returns Pool-id + /// + /// @return pool-id value + /// Pool-id is an unique value that can be used to identify a pool. + uint32_t getId() const { + return (id_); + } + + /// @brief Returns the first address in a pool. + /// + /// @return first address in a pool + const isc::asiolink::IOAddress& getFirstAddress() const { + return (first_); + } + + /// @brief Returns the last address in a pool. + /// @return last address in a pool + const isc::asiolink::IOAddress& getLastAddress() const { + return (last_); + } + + /// @brief Checks if a given address is in the range. + /// + /// @return true, if the address is in pool + bool inRange(const isc::asiolink::IOAddress& addr) const; + +protected: + + /// @brief protected constructor + /// + /// This constructor is protected to prevent anyone from instantiating + /// Pool class directly. Instances of Pool4 and Pool6 should be created + /// instead. + Pool(const isc::asiolink::IOAddress& first, + const isc::asiolink::IOAddress& last); + + /// @brief returns the next unique Pool-ID + /// + /// @return the next unique Pool-ID + static uint32_t getNextID() { + static uint32_t id = 0; + return (id++); + } + + /// @brief pool-id + /// + /// This ID is used to identify this specific pool. + uint32_t id_; + + /// @brief The first address in a pool + isc::asiolink::IOAddress first_; + + /// @brief The last address in a pool + isc::asiolink::IOAddress last_; + + /// @brief Comments field + /// + /// @todo: This field is currently not used. + std::string comments_; +}; + +/// @brief Pool information for IPv6 addresses and prefixes +/// +/// It holds information about pool6, i.e. a range of IPv6 address space that +/// is configured for DHCP allocation. +class Pool6 : public Pool { +public: + + /// @brief specifies Pool type + /// + /// Currently there are 3 pool types defined in DHCPv6: + /// - Non-temporary addresses (conveyed in IA_NA) + /// - Temporary addresses (conveyed in IA_TA) + /// - Delegated Prefixes (conveyed in IA_PD) + /// There is a new one being worked on (IA_PA, see draft-ietf-dhc-host-gen-id), but + /// support for it is not planned for now. + typedef enum { + TYPE_IA, + TYPE_TA, + TYPE_PD + } Pool6Type; + + /// @brief the constructor for Pool6 "min-max" style definition + /// + /// @param first the first address in a pool + /// @param last the last address in a pool + Pool6(Pool6Type type, const isc::asiolink::IOAddress& first, + const isc::asiolink::IOAddress& last); + + /// @brief the constructor for Pool6 "prefix/len" style definition + /// + /// @param prefix specifies prefix of the pool + /// @param prefix_len specifies length of the prefix of the pool + Pool6(Pool6Type type, const isc::asiolink::IOAddress& prefix, + uint8_t prefix_len); + + /// @brief returns pool type + /// + /// @return pool type + Pool6Type getType() const { + return (type_); + } + +private: + /// @brief defines a pool type + Pool6Type type_; + + /// @brief prefix length + /// used by TYPE_PD only (zeroed for other types) + uint8_t prefix_len_; +}; + +/// @brief a pointer an IPv6 Pool +typedef boost::shared_ptr Pool6Ptr; + +/// @brief a container for IPv6 Pools +typedef std::vector Pool6Collection; + +} // end of isc::dhcp namespace +} // end of isc namespace + + +#endif // POOL_H diff --git a/src/lib/dhcp/subnet.cc b/src/lib/dhcp/subnet.cc new file mode 100644 index 0000000000..a999295690 --- /dev/null +++ b/src/lib/dhcp/subnet.cc @@ -0,0 +1,91 @@ +// Copyright (C) 2012 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 +#include +#include +#include + +using namespace isc::asiolink; + +namespace isc { +namespace dhcp { + +Subnet::Subnet(const isc::asiolink::IOAddress& prefix, uint8_t len, + const Triplet& t1, + const Triplet& t2, + const Triplet& valid_lifetime) + :id_(getNextID()), prefix_(prefix), prefix_len_(len), t1_(t1), + t2_(t2), valid_(valid_lifetime) { + if ( (prefix.getFamily() == AF_INET6 && len > 128) || + (prefix.getFamily() == AF_INET && len > 32) ) { + isc_throw(BadValue, "Invalid prefix length specified for subnet: " << len); + } +} + +bool Subnet::inRange(const isc::asiolink::IOAddress& addr) const { + IOAddress first = firstAddrInPrefix(prefix_, prefix_len_); + IOAddress last = lastAddrInPrefix(prefix_, prefix_len_); + + return ((first <= addr) && (addr <= last)); +} + +Subnet6::Subnet6(const isc::asiolink::IOAddress& prefix, uint8_t length, + const Triplet& t1, + const Triplet& t2, + const Triplet& preferred_lifetime, + const Triplet& valid_lifetime) + :Subnet(prefix, length, t1, t2, valid_lifetime), + preferred_(preferred_lifetime){ + if (prefix.getFamily() != AF_INET6) { + isc_throw(BadValue, "Non IPv6 prefix " << prefix.toText() + << " specified in subnet6"); + } +} + +void Subnet6::addPool6(const Pool6Ptr& pool) { + IOAddress first_addr = pool->getFirstAddress(); + IOAddress last_addr = pool->getLastAddress(); + + if (!inRange(first_addr) || !inRange(last_addr)) { + isc_throw(BadValue, "Pool6 (" << first_addr.toText() << "-" << last_addr.toText() + << " does not belong in this (" << prefix_ << "/" << prefix_len_ + << ") subnet6"); + } + + /// @todo: Check that pools do not overlap + + pools_.push_back(pool); +} + +Pool6Ptr Subnet6::getPool6(const isc::asiolink::IOAddress& hint /* = IOAddress("::")*/ ) { + Pool6Ptr candidate; + for (Pool6Collection::iterator pool = pools_.begin(); pool != pools_.end(); ++pool) { + + // if we won't find anything better, then let's just use the first pool + if (!candidate) { + candidate = *pool; + } + + // if the client provided a pool and there's a pool that hint is valid in, + // then let's use that pool + if ((*pool)->inRange(hint)) { + return (*pool); + } + } + return (candidate); +} + +} // end of isc::dhcp namespace +} // end of isc namespace diff --git a/src/lib/dhcp/subnet.h b/src/lib/dhcp/subnet.h new file mode 100644 index 0000000000..aa59010ee6 --- /dev/null +++ b/src/lib/dhcp/subnet.h @@ -0,0 +1,161 @@ +// Copyright (C) 2012 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. + +#ifndef SUBNET_H +#define SUBNET_H + +#include +#include +#include +#include + +namespace isc { +namespace dhcp { + +/// @brief a base class for Subnet4 and Subnet6 +/// +/// This class presents a common base for IPv4 and IPv6 subnets. +/// In a physical sense, a subnet defines a single network link with all devices +/// attached to it. In most cases all devices attached to a single link can +/// share the same parameters. Therefore Subnet holds several values that are +/// typically shared by all hosts: renew timer (T1), rebind timer (T2) and +/// leased addresses lifetime (valid-lifetime). +/// +/// @todo: Implement support for options here +class Subnet { +public: + /// @brief checks if specified address is in range + bool inRange(const isc::asiolink::IOAddress& addr) const; + + /// @brief return valid-lifetime for addresses in that prefix + Triplet getValid() const { + return (valid_); + } + + /// @brief returns T1 (renew timer), expressed in seconds + Triplet getT1() const { + return (t1_); + } + + /// @brief returns T2 (rebind timer), expressed in seconds + Triplet getT2() const { + return (t2_); + } + +protected: + /// @brief protected constructor + // + /// By making the constructor protected, we make sure that noone will + /// ever instantiate that class. Pool4 and Pool6 should be used instead. + Subnet(const isc::asiolink::IOAddress& prefix, uint8_t len, + const Triplet& t1, + const Triplet& t2, + const Triplet& valid_lifetime); + + /// @brief returns the next unique Subnet-ID + /// + /// @return the next unique Subnet-ID + static uint32_t getNextID() { + static uint32_t id = 0; + return (id++); + } + + /// @brief subnet-id + /// + /// Subnet-id is a unique value that can be used to find or identify + /// a Subnet4 or Subnet6. + uint32_t id_; + + /// @brief a prefix of the subnet + isc::asiolink::IOAddress prefix_; + + /// @brief a prefix length of the subnet + uint8_t prefix_len_; + + /// @brief a tripet (min/default/max) holding allowed renew timer values + Triplet t1_; + + /// @brief a tripet (min/default/max) holding allowed rebind timer values + Triplet t2_; + + /// @brief a tripet (min/default/max) holding allowed valid lifetime values + Triplet valid_; +}; + +/// @brief A configuration holder for IPv6 subnet. +/// +/// This class represents an IPv6 subnet. +class Subnet6 : public Subnet { +public: + + /// @brief Constructor with all parameters + /// + /// @param prefix Subnet6 prefix + /// @param length prefix length + /// @param t1 renewal timer (in seconds) + /// @param t2 rebind timer (in seconds) + /// @param preferred_lifetime preferred lifetime of leases (in seconds) + /// @param valid_lifetime preferred lifetime of leases (in seconds) + Subnet6(const isc::asiolink::IOAddress& prefix, uint8_t length, + const Triplet& t1, + const Triplet& t2, + const Triplet& preferred_lifetime, + const Triplet& valid_lifetime); + + /// @brief Returns preverred lifetime (in seconds) + /// + /// @return a triplet with preferred lifetime + Triplet getPreferred() const { + return (preferred_); + } + + /// @brief Returns a pool that specified address belongs to + /// + /// @param hint address that the returned pool should cover (optional) + /// @return Pointer to found pool6 (or NULL) + Pool6Ptr getPool6(const isc::asiolink::IOAddress& hint = + isc::asiolink::IOAddress("::")); + + /// @brief Adds a new pool. + /// @param pool pool to be added + void addPool6(const Pool6Ptr& pool); + + /// @brief returns all pools + /// + /// The reference is only valid as long as the object that + /// returned it. + /// + /// @return a collection of all pools + const Pool6Collection& getPools() const { + return pools_; + } + +protected: + /// @brief collection of pools in that list + Pool6Collection pools_; + + /// @brief a triplet with preferred lifetime (in seconds) + Triplet preferred_; +}; + +/// @brief A pointer to a Subnet6 object +typedef boost::shared_ptr Subnet6Ptr; + +/// @brief A collection of Subnet6 objects +typedef std::vector Subnet6Collection; + +} // end of isc::dhcp namespace +} // end of isc namespace + +#endif // SUBNET_T diff --git a/src/lib/dhcp/triplet.h b/src/lib/dhcp/triplet.h new file mode 100644 index 0000000000..549698babd --- /dev/null +++ b/src/lib/dhcp/triplet.h @@ -0,0 +1,108 @@ +// Copyright (C) 2012 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. + +namespace isc { +namespace dhcp { + +/// @brief this template specifies a parameter value +/// +/// This template class is used to store configuration parameters, like lifetime or T1. +/// It defines 3 parameters: min, default, and max value. There are 2 constructors: +/// - simple (just one value that sets all parameters) +/// - extended (that sets default value and two thresholds) +/// It will be used with integer types. It provides necessary operators, so +/// it can be assigned to a plain integer or integer assigned to a Triplet. +/// See TripletTest.operator test for details on an easy Triplet usage. +template +class Triplet { +public: + + /// @brief base type to Triple conversion + /// + /// Typically: uint32_t to Triplet assignment. It is very convenient + /// to be able to simply write Triplet x = 7; + Triplet operator=(T other) { + min_ = other; + default_ = other; + max_ = other; + return *this; + } + + /// @brief triplet to base type conversion + /// + /// Typically: Triplet to uint32_t assignment. It is very convenient + /// to be able to simply write uint32_t z = x; (where x is a Triplet) + operator T() const { + return (default_); + } + + /// @brief sets a fixed value + /// + /// This constructor assigns a fixed (i.e. no range, just a single value) + /// value. + Triplet(T value) + :min_(value), default_(value), max_(value) { + } + + /// @brief sets the default value and thresholds + /// + /// @throw BadValue if min <= def <= max rule is violated + Triplet(T min, T def, T max) + :min_(min), default_(def), max_(max) { + if ( (min_ > def) || (def > max_) ) { + isc_throw(BadValue, "Invalid triplet values."); + } + } + + /// @brief returns a minimum allowed value + T getMin() const { return min_;} + + /// @brief returns the default value + T get() const { return default_;} + + /// @brief returns value with a hint + /// + /// DHCP protocol treats any values sent by a client as hints. + /// This is a method that implements that. We can assign any value + /// from configured range that client asks. + T get(T hint) const { + if (hint <= min_) { + return (min_); + } + + if (hint >= max_) { + return (max_); + } + + return (hint); + } + + /// @brief returns a maximum allowed value + T getMax() const { return max_; } + +protected: + + /// @brief the minimum value + T min_; + + /// @brief the default value + T default_; + + /// @brief the maximum value + T max_; +}; + + +} // namespace isc::dhcp +} // namespace isc