// Copyright (C) 2010 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 using namespace isc::dns; using namespace isc::auth; using namespace isc::statistics; using namespace isc::auth::statistics; namespace { /// \brief Fill isc::data::ElementPtr with given counter. /// \param counter Counter which stores values to fill /// \param type_tree CounterSpec corresponding to counter for building item /// name /// \param trees isc::data::ElementPtr to be filled in; caller has ownership of /// isc::data::ElementPtr void fillNodes(const Counter& counter, const struct isc::auth::statistics::CounterSpec type_tree[], isc::data::ElementPtr& trees) { using namespace isc::data; for (int i = 0; type_tree[i].name != NULL; ++i) { if (type_tree[i].sub_counters != NULL) { isc::data::ElementPtr sub_counters = Element::createMap(); trees->set(type_tree[i].name, sub_counters); fillNodes(counter, type_tree[i].sub_counters, sub_counters); } else { trees->set(type_tree[i].name, Element::create(static_cast( counter.get(type_tree[i].counter_id))) ); } } } // ### STATISTICS ITEMS DEFINITION ### } // anonymous namespace namespace isc { namespace auth { namespace statistics { // Note: opcode in this array must be start with 0 and be sequential const int opcode_to_msgcounter[] = { MSG_OPCODE_QUERY, // Opcode = 0: Query MSG_OPCODE_IQUERY, // Opcode = 1: IQuery MSG_OPCODE_STATUS, // Opcode = 2: Status MSG_OPCODE_OTHER, // Opcode = 3: (Unassigned) MSG_OPCODE_NOTIFY, // Opcode = 4: Notify MSG_OPCODE_UPDATE, // Opcode = 5: Update MSG_OPCODE_OTHER, // Opcode = 6: (Unassigned) MSG_OPCODE_OTHER, // Opcode = 7: (Unassigned) MSG_OPCODE_OTHER, // Opcode = 8: (Unassigned) MSG_OPCODE_OTHER, // Opcode = 9: (Unassigned) MSG_OPCODE_OTHER, // Opcode = 10: (Unassigned) MSG_OPCODE_OTHER, // Opcode = 11: (Unassigned) MSG_OPCODE_OTHER, // Opcode = 12: (Unassigned) MSG_OPCODE_OTHER, // Opcode = 13: (Unassigned) MSG_OPCODE_OTHER, // Opcode = 14: (Unassigned) MSG_OPCODE_OTHER // Opcode = 15: (Unassigned) }; const size_t num_opcode_to_msgcounter = sizeof(opcode_to_msgcounter) / sizeof(opcode_to_msgcounter[0]); // Note: rcode in this array must be start with 0 and be sequential const int rcode_to_msgcounter[] = { MSG_RCODE_NOERROR, // Rcode = 0: NoError MSG_RCODE_FORMERR, // Rcode = 1: FormErr MSG_RCODE_SERVFAIL, // Rcode = 2: ServFail MSG_RCODE_NXDOMAIN, // Rcode = 3: NXDomain MSG_RCODE_NOTIMP, // Rcode = 4: NotImp MSG_RCODE_REFUSED, // Rcode = 5: Refused MSG_RCODE_YXDOMAIN, // Rcode = 6: YXDomain MSG_RCODE_YXRRSET, // Rcode = 7: YXRRSet MSG_RCODE_NXRRSET, // Rcode = 8: NXRRSet MSG_RCODE_NOTAUTH, // Rcode = 9: NotAuth MSG_RCODE_NOTZONE, // Rcode = 10: NotZone MSG_RCODE_OTHER, // Rcode = 11: (Unassigned) MSG_RCODE_OTHER, // Rcode = 12: (Unassigned) MSG_RCODE_OTHER, // Rcode = 13: (Unassigned) MSG_RCODE_OTHER, // Rcode = 14: (Unassigned) MSG_RCODE_OTHER, // Rcode = 15: (Unassigned) MSG_RCODE_BADVERS // Rcode = 16: BADVERS }; const size_t num_rcode_to_msgcounter = sizeof(rcode_to_msgcounter) / sizeof(rcode_to_msgcounter[0]); Counters::Counters() : server_msg_counter_(MSG_COUNTER_TYPES) {} void Counters::incRequest(const MessageAttributes& msgattrs) { // protocols carrying request if (msgattrs.getRequestIPVersion() == AF_INET) { server_msg_counter_.inc(MSG_REQUEST_IPV4); } else if (msgattrs.getRequestIPVersion() == AF_INET6) { server_msg_counter_.inc(MSG_REQUEST_IPV6); } if (msgattrs.getRequestTransportProtocol() == IPPROTO_UDP) { server_msg_counter_.inc(MSG_REQUEST_UDP); } else if (msgattrs.getRequestTransportProtocol() == IPPROTO_TCP) { server_msg_counter_.inc(MSG_REQUEST_TCP); } // Opcode const boost::optional& opcode = msgattrs.getRequestOpCode(); // Increment opcode counter only if the opcode exists; opcode can be empty // if a short message which does not contain DNS header is received, or // a response message (i.e. QR bit is set) is received. if (opcode) { server_msg_counter_.inc(opcode_to_msgcounter[opcode->getCode()]); if (opcode.get() == Opcode::QUERY()) { // Recursion Desired bit if (msgattrs.requestHasRD()) { server_msg_counter_.inc(MSG_QRYRECURSION); } } } // TSIG if (msgattrs.requestHasTSIG()) { server_msg_counter_.inc(MSG_REQUEST_TSIG); } if (msgattrs.requestHasBadSig()) { server_msg_counter_.inc(MSG_REQUEST_BADSIG); // If signature validation failed, no other request attributes (except // for opcode) are reliable. Skip processing of the rest of request // counters. return; } // EDNS0 if (msgattrs.requestHasEDNS0()) { server_msg_counter_.inc(MSG_REQUEST_EDNS0); } // DNSSEC OK bit if (msgattrs.requestHasDO()) { server_msg_counter_.inc(MSG_REQUEST_DNSSEC_OK); } } void Counters::incResponse(const MessageAttributes& msgattrs, const Message& response) { // responded server_msg_counter_.inc(MSG_RESPONSE); // response truncated if (msgattrs.responseIsTruncated()) { server_msg_counter_.inc(MSG_RESPONSE_TRUNCATED); } // response EDNS ConstEDNSPtr response_edns = response.getEDNS(); if (response_edns && response_edns->getVersion() == 0) { server_msg_counter_.inc(MSG_RESPONSE_EDNS0); } // response TSIG if (msgattrs.responseHasTSIG()) { server_msg_counter_.inc(MSG_RESPONSE_TSIG); } // response SIG(0) is currently not implemented // RCODE const unsigned int rcode = response.getRcode().getCode(); const unsigned int rcode_type = rcode < num_rcode_to_msgcounter ? rcode_to_msgcounter[rcode] : MSG_RCODE_OTHER; server_msg_counter_.inc(rcode_type); // Unsupported EDNS version if (rcode == Rcode::BADVERS().getCode()) { server_msg_counter_.inc(MSG_REQUEST_BADEDNSVER); } const boost::optional& opcode = msgattrs.getRequestOpCode(); if (!opcode) { isc_throw(isc::Unexpected, "Opcode of the request is empty while it is" " responded"); } if (!msgattrs.requestHasBadSig() && opcode.get() == Opcode::QUERY()) { // compound attributes const unsigned int answer_rrs = response.getRRCount(Message::SECTION_ANSWER); const bool is_aa_set = response.getHeaderFlag(Message::HEADERFLAG_AA); if (is_aa_set) { // QryAuthAns server_msg_counter_.inc(MSG_QRYAUTHANS); } else { // QryNoAuthAns server_msg_counter_.inc(MSG_QRYNOAUTHANS); } if (rcode == Rcode::NOERROR_CODE) { if (answer_rrs > 0) { // QrySuccess server_msg_counter_.inc(MSG_QRYSUCCESS); } else { if (is_aa_set) { // QryNxrrset server_msg_counter_.inc(MSG_QRYNXRRSET); } else { // QryReferral server_msg_counter_.inc(MSG_QRYREFERRAL); } } } else if (rcode == Rcode::REFUSED_CODE) { if (!response.getHeaderFlag(Message::HEADERFLAG_RD)) { // AuthRej server_msg_counter_.inc(MSG_QRYREJECT); } } } } void Counters::inc(const MessageAttributes& msgattrs, const Message& response, const bool done) { // increment request counters incRequest(msgattrs); if (done) { // increment response counters if answer was sent incResponse(msgattrs, response); } } Counters::ConstItemTreePtr Counters::get() const { using namespace isc::data; isc::data::ElementPtr item_tree = Element::createMap(); isc::data::ElementPtr zones = Element::createMap(); item_tree->set("zones", zones); isc::data::ElementPtr server = Element::createMap(); fillNodes(server_msg_counter_, msg_counter_tree, server); zones->set("_SERVER_", server); return (item_tree); } } // namespace statistics } // namespace auth } // namespace isc