Commit ccb253d8 authored by Kean Johnston's avatar Kean Johnston
Browse files

Merge branch 'master' of ssh://git.bind10.isc.org/var/bind10/git/bind10

Conflicts:
	ChangeLog
parents f8fac882 75867fdf
702. [bug] kean
703. [bug] kean
A bug in b10-msgq was fixed where it would remove the socket file if
there was an existing copy of b10-msgq running. It now correctly
detects and reports this without removing the socket file.
(Trac #433, git c18a49b0435c656669e6f87ef65d44dc98e0e726)
702. [func] marcin
perfdhcp: support for sending DHCPv6 Renew messages at the specified
rate and measure performance.
(Trac #3183, git 66f2939830926f4337623b159210103b5a8e2434)
701. [bug] tomek
libdhcp++: Incoming DHCPv6 IAPREFIX option is now parsed properly.
(Trac #3211, git ed43618a2c7b2387d76f99a5a4b1a3e05ac70f5e)
......@@ -69,7 +74,7 @@
libdhcp++: Created definitions for standard DHCPv4 options:
tftp-server-name (66) and boot-file-name (67). Also, fixed definition
of DHCPv4 option time-offset (2).
(Trac #3199, git abcd)
(Trac #3199, git 6e171110c4dd9ae3b1be828b9516efc65c33460b)
690. [bug] tomek
b10-dhcp4: Relay Agent Info option is now echoed back in
......
......@@ -23,6 +23,7 @@ perfdhcp_SOURCES += command_options.cc command_options.h
perfdhcp_SOURCES += localized_option.h
perfdhcp_SOURCES += perf_pkt6.cc perf_pkt6.h
perfdhcp_SOURCES += perf_pkt4.cc perf_pkt4.h
perfdhcp_SOURCES += packet_storage.h
perfdhcp_SOURCES += pkt_transform.cc pkt_transform.h
perfdhcp_SOURCES += stats_mgr.h
perfdhcp_SOURCES += test_control.cc test_control.h
......
......@@ -12,19 +12,21 @@
// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
// PERFORMANCE OF THIS SOFTWARE.
#include "command_options.h"
#include <exceptions/exceptions.h>
#include <dhcp/iface_mgr.h>
#include <dhcp/duid.h>
#include <boost/lexical_cast.hpp>
#include <boost/date_time/posix_time/posix_time.hpp>
#include <config.h>
#include <sstream>
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <unistd.h>
#include <boost/lexical_cast.hpp>
#include <boost/date_time/posix_time/posix_time.hpp>
#include <exceptions/exceptions.h>
#include <dhcp/iface_mgr.h>
#include <dhcp/duid.h>
#include "command_options.h"
using namespace std;
using namespace isc;
......@@ -111,6 +113,7 @@ CommandOptions::reset() {
exchange_mode_ = DORA_SARR;
lease_type_.set(LeaseType::ADDRESS);
rate_ = 0;
renew_rate_ = 0;
report_delay_ = 0;
clients_num_ = 0;
mac_template_.assign(mac, mac + 6);
......@@ -208,7 +211,7 @@ CommandOptions::initialize(int argc, char** argv, bool print_cmd_line) {
// In this section we collect argument values from command line
// they will be tuned and validated elsewhere
while((opt = getopt(argc, argv, "hv46r:t:R:b:n:p:d:D:l:P:a:L:"
"s:iBc1T:X:O:E:S:I:x:w:e:")) != -1) {
"s:iBc1T:X:O:E:S:I:x:w:e:f:")) != -1) {
stream << " -" << static_cast<char>(opt);
if (optarg) {
stream << " " << optarg;
......@@ -299,6 +302,11 @@ CommandOptions::initialize(int argc, char** argv, bool print_cmd_line) {
" must not be a negative integer");
break;
case 'f':
renew_rate_ = positiveInteger("value of the renew rate: -f<renew-rate>"
" must be a positive integer");
break;
case 'h':
usage();
return (true);
......@@ -680,6 +688,8 @@ CommandOptions::validate() const {
"-B is not compatible with IPv6 (-6)");
check((getIpVersion() != 6) && (isRapidCommit() != 0),
"-6 (IPv6) must be set to use -c");
check((getIpVersion() != 6) && (getRenewRate() !=0),
"-f<renew-rate> may be used with -6 (IPv6) only");
check((getExchangeMode() == DO_SA) && (getNumRequests().size() > 1),
"second -n<num-request> is not compatible with -i");
check((getIpVersion() == 4) && !getLeaseType().is(LeaseType::ADDRESS),
......@@ -692,44 +702,52 @@ CommandOptions::validate() const {
"second -d<drop-time> is not compatible with -i");
check((getExchangeMode() == DO_SA) &&
((getMaxDrop().size() > 1) || (getMaxDropPercentage().size() > 1)),
"second -D<max-drop> is not compatible with -i\n");
"second -D<max-drop> is not compatible with -i");
check((getExchangeMode() == DO_SA) && (isUseFirst()),
"-1 is not compatible with -i\n");
"-1 is not compatible with -i");
check((getExchangeMode() == DO_SA) && (getTemplateFiles().size() > 1),
"second -T<template-file> is not compatible with -i\n");
"second -T<template-file> is not compatible with -i");
check((getExchangeMode() == DO_SA) && (getTransactionIdOffset().size() > 1),
"second -X<xid-offset> is not compatible with -i\n");
"second -X<xid-offset> is not compatible with -i");
check((getExchangeMode() == DO_SA) && (getRandomOffset().size() > 1),
"second -O<random-offset is not compatible with -i\n");
"second -O<random-offset is not compatible with -i");
check((getExchangeMode() == DO_SA) && (getElapsedTimeOffset() >= 0),
"-E<time-offset> is not compatible with -i\n");
"-E<time-offset> is not compatible with -i");
check((getExchangeMode() == DO_SA) && (getServerIdOffset() >= 0),
"-S<srvid-offset> is not compatible with -i\n");
"-S<srvid-offset> is not compatible with -i");
check((getExchangeMode() == DO_SA) && (getRequestedIpOffset() >= 0),
"-I<ip-offset> is not compatible with -i\n");
"-I<ip-offset> is not compatible with -i");
check((getExchangeMode() == DO_SA) && (getRenewRate() != 0),
"-f<renew-rate> is not compatible with -i");
check((getExchangeMode() != DO_SA) && (isRapidCommit() != 0),
"-i must be set to use -c\n");
"-i must be set to use -c");
check((getRate() == 0) && (getReportDelay() != 0),
"-r<rate> must be set to use -t<report>\n");
"-r<rate> must be set to use -t<report>");
check((getRate() == 0) && (getNumRequests().size() > 0),
"-r<rate> must be set to use -n<num-request>\n");
"-r<rate> must be set to use -n<num-request>");
check((getRate() == 0) && (getPeriod() != 0),
"-r<rate> must be set to use -p<test-period>\n");
"-r<rate> must be set to use -p<test-period>");
check((getRate() == 0) &&
((getMaxDrop().size() > 0) || getMaxDropPercentage().size() > 0),
"-r<rate> must be set to use -D<max-drop>\n");
"-r<rate> must be set to use -D<max-drop>");
check((getRate() != 0) && (getRenewRate() > getRate()),
"Renew rate specified as -f<renew-rate> must not be greater than"
" the rate specified as -r<rate>");
check((getRate() == 0) && (getRenewRate() != 0),
"Renew rate specified as -f<renew-rate> must not be specified"
" when -r<rate> parameter is not specified");
check((getTemplateFiles().size() < getTransactionIdOffset().size()),
"-T<template-file> must be set to use -X<xid-offset>\n");
"-T<template-file> must be set to use -X<xid-offset>");
check((getTemplateFiles().size() < getRandomOffset().size()),
"-T<template-file> must be set to use -O<random-offset>\n");
"-T<template-file> must be set to use -O<random-offset>");
check((getTemplateFiles().size() < 2) && (getElapsedTimeOffset() >= 0),
"second/request -T<template-file> must be set to use -E<time-offset>\n");
"second/request -T<template-file> must be set to use -E<time-offset>");
check((getTemplateFiles().size() < 2) && (getServerIdOffset() >= 0),
"second/request -T<template-file> must be set to "
"use -S<srvid-offset>\n");
"use -S<srvid-offset>");
check((getTemplateFiles().size() < 2) && (getRequestedIpOffset() >= 0),
"second/request -T<template-file> must be set to "
"use -I<ip-offset>\n");
"use -I<ip-offset>");
}
......@@ -737,6 +755,8 @@ void
CommandOptions::check(bool condition, const std::string& errmsg) const {
// The same could have been done with macro or just if statement but
// we prefer functions to macros here
std::ostringstream stream;
stream << errmsg << "\n";
if (condition) {
isc_throw(isc::InvalidParameter, errmsg);
}
......@@ -793,6 +813,9 @@ CommandOptions::printCommandLine() const {
if (rate_ != 0) {
std::cout << "rate[1/s]=" << rate_ << std::endl;
}
if (getRenewRate() != 0) {
std::cout << "renew-rate[1/s]=" << getRenewRate() << std::endl;
}
if (report_delay_ != 0) {
std::cout << "report[s]=" << report_delay_ << std::endl;
}
......@@ -875,13 +898,14 @@ CommandOptions::printCommandLine() const {
void
CommandOptions::usage() const {
std::cout <<
"perfdhcp [-hv] [-4|-6] [-e<lease-type>] [-r<rate>] [-t<report>]\n"
" [-R<range>] [-b<base>] [-n<num-request>] [-p<test-period>]\n"
" [-d<drop-time>] [-D<max-drop>] [-l<local-addr|interface>]\n"
" [-P<preload>] [-a<aggressivity>] [-L<local-port>] [-s<seed>]\n"
" [-i] [-B] [-c] [-1] [-T<template-file>] [-X<xid-offset>]\n"
" [-O<random-offset] [-E<time-offset>] [-S<srvid-offset>]\n"
" [-I<ip-offset>] [-x<diagnostic-selector>] [-w<wrapped>] [server]\n"
"perfdhcp [-hv] [-4|-6] [-e<lease-type>] [-r<rate>] [-f<renew-rate>]\n"
" [-t<report>] [-R<range>] [-b<base>] [-n<num-request>]\n"
" [-p<test-period>] [-d<drop-time>] [-D<max-drop>]\n"
" [-l<local-addr|interface>] [-P<preload>] [-a<aggressivity>]\n"
" [-L<local-port>] [-s<seed>] [-i] [-B] [-c] [-1]\n"
" [-T<template-file>] [-X<xid-offset>] [-O<random-offset]\n"
" [-E<time-offset>] [-S<srvid-offset>] [-I<ip-offset>]\n"
" [-x<diagnostic-selector>] [-w<wrapped>] [server]\n"
"\n"
"The [server] argument is the name/address of the DHCP server to\n"
"contact. For DHCPv4 operation, exchanges are initiated by\n"
......@@ -924,6 +948,10 @@ CommandOptions::usage() const {
"-E<time-offset>: Offset of the (DHCPv4) secs field / (DHCPv6)\n"
" elapsed-time option in the (second/request) template.\n"
" The value 0 disables it.\n"
"-f<renew-rate>: A rate at which IPv6 Renew requests are sent to\n"
" a server. This value must not be equal or lower than the rate\n"
" specified as -r<rate>. If -r<rate> is not specified, this\n"
" parameter must not be specified too.\n"
"-h: Print this help.\n"
"-i: Do only the initial part of an exchange: DO or SA, depending on\n"
" whether -6 is given.\n"
......
......@@ -16,11 +16,12 @@
#ifndef COMMAND_OPTIONS_H
#define COMMAND_OPTIONS_H
#include <boost/noncopyable.hpp>
#include <stdint.h>
#include <string>
#include <vector>
#include <boost/noncopyable.hpp>
namespace isc {
namespace perfdhcp {
......@@ -155,6 +156,11 @@ public:
/// \return exchange rate per second.
int getRate() const { return rate_; }
/// \brief Returns a rate at which IPv6 Renew messages are sent.
///
/// \return A rate at which IPv6 Renew messages are sent.
int getRenewRate() const { return (renew_rate_); }
/// \brief Returns delay between two performance reports.
///
/// \return delay between two consecutive performance reports.
......@@ -461,6 +467,8 @@ private:
LeaseType lease_type_;
/// Rate in exchange per second
int rate_;
/// A rate at which DHCPv6 Renew messages are sent.
int renew_rate_;
/// Delay between generation of two consecutive
/// performance reports
int report_delay_;
......
// Copyright (C) 2013 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 PACKET_STORAGE_H
#define PACKET_STORAGE_H
#include <boost/noncopyable.hpp>
#include <boost/shared_ptr.hpp>
#include <list>
#include <stdint.h>
namespace isc {
namespace perfdhcp {
/// \brief Represents a list of packets with a sequential and random access to
/// list elements.
///
/// The main purpose of this class is to support sending Renew and Release
/// messages from perfdhcp. The Renew and Release messages are sent for existing
/// leases only. Therefore, the typical use case for this class is that it holds
/// a list of Reply messages sent by the server in response to Request messages.
/// The Request messages hold addresses and/or IPv6 prefixes acquired so they
/// can be used to identify existing leases. When perfdhcp needs to send Renew
/// or Release message, it will access one of the elements on this list and
/// will create the Renew or Release message based on its content. Once the
/// element (packet) is returned it is also deleted from the list, so as it is
/// not used again. This class provide either sequential access to the packets
/// or random access. The random access algorithm is much slower but at least
/// it allows to simulate more real scenario when the renewing or releasing
/// client is random.
///
/// \tparam Pkt4 or Pkt6 class, which represents DHCPv4 or DHCPv6 message
/// respectively.
///
/// \note Although the class is intended to hold Pkt4 and Pkt6 objects, the
/// current implementation is generic enough to holds any object wrapped in the
/// boost::shared_ptr.
template<typename T>
class PacketStorage : public boost::noncopyable {
public:
/// A type which represents the pointer to a packet.
typedef boost::shared_ptr<T> PacketPtr;
private:
/// An internal container actually holding packets.
typedef typename std::list<PacketPtr> PacketContainer;
/// An iterator to the element in the internal container.
typedef typename PacketContainer::iterator PacketContainerIterator;
public:
/// \brief Constructor.
PacketStorage() { }
/// \brief Appends the new packet object to the collection.
///
/// \param packet A pointer to an object representing a packet.
void append(const PacketPtr& packet) {
storage_.push_back(packet);
if (storage_.size() == 1) {
current_pointer_ = storage_.begin();
}
}
/// \brief Removes packets from the storage.
///
/// It is possible to specify a number of packets to be removed
/// from a storage. Packets are removed from the beginning of the
/// storage. If specified number is greater than the size of the
/// storage, all packets are removed.
///
/// @param num A number of packets to be removed. If omitted,
/// all packets will be removed.
void clear(const uint64_t num = 0) {
if (num != 0) {
PacketContainerIterator last = storage_.begin();
std::advance(last, num > size() ? size() : num);
current_pointer_ = storage_.erase(storage_.begin(), last);
} else {
storage_.clear();
current_pointer_ = storage_.begin();
}
}
/// \brief Checks if the storage has no packets.
///
/// \return true if storage is empty, false otherwise.
bool empty() const {
return (storage_.empty());
}
/// \brief Returns next packet from the storage.
///
/// This function returns packets sequentially (in the same order
/// in which they have been appended). The returned packet is
/// instantly removed from the storage.
///
/// \return next packet from the storage.
PacketPtr getNext() {
if (storage_.empty()) {
return (PacketPtr());
} else if (current_pointer_ == storage_.end()) {
current_pointer_ = storage_.begin();
}
PacketPtr packet = *current_pointer_;
current_pointer_ = storage_.erase(current_pointer_);
return (packet);
}
/// \brief Returns random packet from the storage.
///
/// This function picks random packet from the storage and returns
/// it. It is way slower than the @c getNext function because it has to
/// iterate over all existing entries from the beginning of the storage
/// to the random packet's position. Therefore, care should be taken
/// when using this function to access elements when storage is large.
///
/// \return random packet from the storage.
PacketPtr getRandom() {
if (empty()) {
return (PacketPtr());
}
current_pointer_ = storage_.begin();
if (size() > 1) {
std::advance(current_pointer_, rand() % (size() - 1));
}
PacketPtr packet = *current_pointer_;
current_pointer_ = storage_.erase(current_pointer_);
return (packet);
}
/// \brief Returns number of packets in the storage.
///
/// \return number of packets in the storage.
uint64_t size() const {
return (storage_.size());
}
private:
std::list<PacketPtr> storage_; ///< Holds all appended packets.
PacketContainerIterator current_pointer_; ///< Holds the iterator to the
///< next element returned.
};
} // namespace perfdhcp
} // namespace isc
#endif // PACKET_STORAGE_H
......@@ -15,8 +15,9 @@
#ifndef STATS_MGR_H
#define STATS_MGR_H
#include <iostream>
#include <map>
#include <dhcp/pkt4.h>
#include <dhcp/pkt6.h>
#include <exceptions/exceptions.h>
#include <boost/noncopyable.hpp>
#include <boost/shared_ptr.hpp>
......@@ -27,7 +28,9 @@
#include <boost/multi_index/mem_fun.hpp>
#include <boost/date_time/posix_time/posix_time.hpp>
#include <exceptions/exceptions.h>
#include <iostream>
#include <map>
namespace isc {
namespace perfdhcp {
......@@ -120,7 +123,8 @@ public:
XCHG_DO, ///< DHCPv4 DISCOVER-OFFER
XCHG_RA, ///< DHCPv4 REQUEST-ACK
XCHG_SA, ///< DHCPv6 SOLICIT-ADVERTISE
XCHG_RR ///< DHCPv6 REQUEST-REPLY
XCHG_RR, ///< DHCPv6 REQUEST-REPLY
XCHG_RN ///< DHCPv6 RENEW-REPLY
};
/// \brief Exchange Statistics.
......@@ -1165,6 +1169,8 @@ public:
return("SOLICIT-ADVERTISE");
case XCHG_RR:
return("REQUEST-REPLY");
case XCHG_RN:
return("RENEW-REPLY");
default:
return("Unknown exchange type");
}
......
......@@ -97,6 +97,38 @@ TestControl::TestControl() {
reset();
}
void
TestControl::cleanCachedPackets() {
CommandOptions& options = CommandOptions::instance();
// When Renews are not sent, Reply packets are not cached so there
// is nothing to do.
if (options.getRenewRate() == 0) {
return;
}
static boost::posix_time::ptime last_clean =
microsec_clock::universal_time();
// Check how much time has passed since last cleanup.
time_period time_since_clean(last_clean,
microsec_clock::universal_time());
// Cleanup every 1 second.
if (time_since_clean.length().total_seconds() >= 1) {
// Calculate how many cached packets to remove. Actually we could
// just leave enough packets to handle Renews for 1 second but
// since we want to randomize leases to be renewed so leave 5
// times more packets to randomize from.
// @todo The cache size might be controlled from the command line.
if (reply_storage_.size() > 5 * options.getRenewRate()) {
reply_storage_.clear(reply_storage_.size() -
5 * options.getRenewRate());
}
// Remember when we performed a cleanup for the last time.
// We want to do the next cleanup not earlier than in one second.
last_clean = microsec_clock::universal_time();
}
}
void
TestControl::copyIaOptions(const Pkt6Ptr& pkt_from, Pkt6Ptr& pkt_to) {
if (!pkt_from || !pkt_to) {
......@@ -283,6 +315,31 @@ TestControl::checkExitConditions() const {
return (false);
}
Pkt6Ptr
TestControl::createRenew(const Pkt6Ptr& reply) {
if (!reply) {
isc_throw(isc::BadValue,"Unable to create Renew packet from the Reply packet"
" because the instance of the Reply is NULL");
}
Pkt6Ptr renew(new Pkt6(DHCPV6_RENEW, generateTransid()));
// Client id.
OptionPtr opt_clientid = reply->getOption(D6O_CLIENTID);
if (!opt_clientid) {
isc_throw(isc::Unexpected, "failed to create Renew packet because client id"
" option has not been found in the Reply from the server");
}
renew->addOption(opt_clientid);
// Server id.
OptionPtr opt_serverid = reply->getOption(D6O_SERVERID);
if (!opt_serverid) {
isc_throw(isc::Unexpected, "failed to create Renew packet because server id"
" option has not been found in the Reply from the server");
}
renew->addOption(opt_serverid);
copyIaOptions(reply, renew);
return (renew);
}
OptionPtr
TestControl::factoryElapsedTime6(Option::Universe, uint16_t,
const OptionBuffer& buf) {
......@@ -426,6 +483,24 @@ TestControl::generateDuid(uint8_t& randomized) const {
return (duid);
}
uint32_t
TestControl::getCurrentTimeout() const {
CommandOptions& options = CommandOptions::instance();
ptime now(microsec_clock::universal_time());
// Check that we haven't passed the moment to send the next set of
// packets.
if (now >= send_due_ ||
(options.getRenewRate() != 0 && now >= renew_due_)) {
return (0);
}
// There is a due time to send Solicit and Renew. We should adjust
// the timeout to the due time which occurs sooner.
ptime due = send_due_ > renew_due_ ? renew_due_ : send_due_;
time_period due_period(now, due);
return (due_period.length().total_microseconds());
}
int
TestControl::getElapsedTimeOffset() const {
int elp_offset = CommandOptions::instance().getIpVersion() == 4 ?
......@@ -456,17 +531,18 @@ TestControl::getElapsedTime(const T& pkt1, const T& pkt2) {
uint64_t
TestControl::getNextExchangesNum() const {
TestControl::getNextExchangesNum(const boost::posix_time::ptime& send_due,
const int rate) {
CommandOptions& options = CommandOptions::instance();
// Get current time.
ptime now(microsec_clock::universal_time());
if (now >= send_due_) {
if (now >= send_due) {
// Reset number of exchanges.
uint64_t due_exchanges = 0;
// If rate is specified from the command line we have to
// synchornize with it.
if (options.getRate() != 0) {
time_period period(send_due_, now);
if (rate != 0) {
time_period period(send_due, now);
time_duration duration = period.length();
// due_factor indicates the number of seconds that
// sending next chunk of packets will take.
......@@ -475,7 +551,7 @@ TestControl::getNextExchangesNum() const {
due_factor += duration.total_seconds();
// Multiplying due_factor by expected rate gives the number
// of exchanges to be initiated.
due_exchanges = static_cast<uint64_t>(due_factor * options.getRate());
due_exchanges = static_cast<uint64_t>(due_factor * rate);
// We want to make sure that at least one packet goes out.
if (due_exchanges == 0) {
due_exchanges = 1;
......@@ -616,6 +692,9 @@ TestControl::initializeStatsMgr() {
stats_mgr6_->addExchangeStats(StatsMgr6::XCHG_RR,
options.getDropTime()[1]);
}
if (options.getRenewRate() != 0) {
stats_mgr6_->addExchangeStats(StatsMgr6::XCHG_RN);
}
}
if (testDiags('i')) {
if (options.getIpVersion() == 4) {
......@@ -769,6 +848,17 @@ TestControl::sendPackets(const TestControlSocket& socket,
}
}
uint64_t
TestControl::sendRenewPackets(const TestControlSocket& socket,
const uint64_t packets_num) {
for (uint64_t i = 0; i < packets_num; ++i) {
if (!sendRenew(socket)) {
return (i);
}
}
return (packets_num);
}
void
TestControl::printDiagnostics() const {
CommandOptions& options = CommandOptions::instance();
......@@ -1024,20 +1114,27 @@ TestControl::processReceivedPacket6(const TestControlSocket& socket,
}
}
} else if (packet_type == DHCPV6_REPLY) {
stats_mgr6_->passRcvdPacket(StatsMgr6::XCHG_RR, pkt6);