added option for GRTT probing aspect of NORM to use a distinct IP traffic class for its operation. The reason for this is where some Layer 2 protocols may implement some form of reliable, retransmission that could adversely impact the NORM GRTT round-trip measurement. A distinct traffic class for that signaling allows the possibility of mapping it to different underlying Layer 2 services (e.g. bypass any reliability mechanism for those specific NORM messages)

pull/18/head
bebopagogo 2020-06-06 12:41:58 -04:00
parent 5fbeef6fae
commit c738eea1a9
11 changed files with 374 additions and 151 deletions

View File

@ -144,6 +144,10 @@ class NormCaster
} }
void SetSilentReceiver(bool state) void SetSilentReceiver(bool state)
{NormSetSilentReceiver(norm_session, true);} {NormSetSilentReceiver(norm_session, true);}
void SetProbeTOS(UINT8 value)
{probe_tos = value;}
void SetTxLoss(double txloss) void SetTxLoss(double txloss)
{NormSetTxLoss(norm_session, txloss);} {NormSetTxLoss(norm_session, txloss);}
@ -156,6 +160,7 @@ class NormCaster
unsigned int tx_pending_prefix_len; unsigned int tx_pending_prefix_len;
bool is_multicast; bool is_multicast;
bool loopback; bool loopback;
UINT8 probe_tos;
unsigned int norm_tx_segment_size; unsigned int norm_tx_segment_size;
unsigned int norm_tx_queue_max; // max number of objects that can be enqueued at once unsigned int norm_tx_queue_max; // max number of objects that can be enqueued at once
unsigned int norm_tx_queue_count; // count of unacknowledged enqueued objects (TBD - optionally track size too) unsigned int norm_tx_queue_count; // count of unacknowledged enqueued objects (TBD - optionally track size too)
@ -178,7 +183,7 @@ class NormCaster
NormCaster::NormCaster() NormCaster::NormCaster()
: norm_session(NORM_SESSION_INVALID), tx_file_iterator(tx_file_list), : norm_session(NORM_SESSION_INVALID), tx_file_iterator(tx_file_list),
tx_pending_prefix_len(0), tx_pending_prefix_len(0),
is_multicast(false), loopback(false), norm_tx_segment_size(1400), is_multicast(false), loopback(false), probe_tos(0), norm_tx_segment_size(1400),
norm_tx_queue_max(8), norm_tx_queue_count(0), norm_tx_queue_max(8), norm_tx_queue_count(0),
norm_flow_control_pending(false), norm_tx_vacancy(true), norm_acking(false), norm_flow_control_pending(false), norm_tx_vacancy(true), norm_acking(false),
norm_flushing(true), norm_flush_object(NORM_OBJECT_INVALID), norm_last_object(NORM_OBJECT_INVALID), norm_flushing(true), norm_flush_object(NORM_OBJECT_INVALID), norm_last_object(NORM_OBJECT_INVALID),
@ -243,6 +248,8 @@ bool NormCaster::OpenNormSession(NormInstanceHandle instance, const char* addr,
//NormSetTxRobustFactor(norm_session, 20); //NormSetTxRobustFactor(norm_session, 20);
NormSetGrttProbingTOS(norm_session, probe_tos);
return true; return true;
} // end NormCaster::OpenNormSession() } // end NormCaster::OpenNormSession()
@ -662,7 +669,7 @@ void Usage()
{ {
fprintf(stderr, "Usage: normCast id <nodeId> {send <file/dir list> &| recv <rxCacheDir>}\n" fprintf(stderr, "Usage: normCast id <nodeId> {send <file/dir list> &| recv <rxCacheDir>}\n"
" [addr <addr>[/<port>]][interface <name>][ack <node1>[,<node2>,...]\n" " [addr <addr>[/<port>]][interface <name>][ack <node1>[,<node2>,...]\n"
" [cc|cce|ccl|rate <bitsPerSecond>]\n" " [cc|cce|ccl|rate <bitsPerSecond>][ptos <value>]\n"
" [debug <level>][trace]\n"); " [debug <level>][trace]\n");
} // end Usage() } // end Usage()
@ -746,6 +753,27 @@ int main(int argc, char* argv[])
{ {
loopback = true; loopback = true;
} }
else if (0 == strncmp(cmd, "ptos", len))
{
if (i >= argc)
{
fprintf(stderr, "normStreamer error: missing 'ptos' value!\n");
Usage();
return -1;
}
int tos = -1;
int result = sscanf(argv[i], "%i", &tos);
if (1 != result)
result = sscanf(argv[i], "%x", &tos);
if ((1 != result) || (tos < 0) || (tos > 255))
{
fprintf(stderr, "normStreamer error: invalid 'ptos' value!\n");
Usage();
return -1;
}
i++;
normCast.SetProbeTOS(tos);
}
else if (0 == strncmp(cmd, "addr", len)) else if (0 == strncmp(cmd, "addr", len))
{ {
if (i >= argc) if (i >= argc)

View File

@ -279,6 +279,9 @@ class NormStreamer
void SetOutputSocketBufferSize(unsigned int value) void SetOutputSocketBufferSize(unsigned int value)
{output_socket_buffer_size = value;} {output_socket_buffer_size = value;}
void SetProbeTOS(UINT8 value)
{probe_tos = value;}
// Check that sequence numbers increase by one each time. // Check that sequence numbers increase by one each time.
// Assumes that sequence number is 8- or 4-byte network-order first 8 bytes of buffer. // Assumes that sequence number is 8- or 4-byte network-order first 8 bytes of buffer.
void CheckSequenceNumber(const char* buffer, const char* source); void CheckSequenceNumber(const char* buffer, const char* source);
@ -290,6 +293,7 @@ class NormStreamer
private: private:
NormSessionHandle norm_session; NormSessionHandle norm_session;
bool is_multicast; bool is_multicast;
bool probe_tos;
bool loopback; bool loopback;
bool is_running; bool is_running;
@ -359,7 +363,7 @@ class NormStreamer
}; // end class NormStreamer }; // end class NormStreamer
NormStreamer::NormStreamer() NormStreamer::NormStreamer()
: norm_session(NORM_SESSION_INVALID), is_multicast(false), loopback(false), is_running(false), : norm_session(NORM_SESSION_INVALID), is_multicast(false), probe_tos(0), loopback(false), is_running(false),
input_socket(ProtoSocket::UDP), input_file(stdin), input_fd(fileno(stdin)), input_ready(true), input_socket(ProtoSocket::UDP), input_file(stdin), input_fd(fileno(stdin)), input_ready(true),
input_needed(false), input_msg_length(0), input_index(0), input_needed(false), input_msg_length(0), input_index(0),
tx_stream (NORM_OBJECT_INVALID), tx_ready(true), tx_stream (NORM_OBJECT_INVALID), tx_ready(true),
@ -570,6 +574,8 @@ bool NormStreamer::OpenNormSession(NormInstanceHandle instance, const char* addr
NormSetTxRobustFactor(norm_session, 20); NormSetTxRobustFactor(norm_session, 20);
NormSetGrttProbingTOS(norm_session, probe_tos);
return true; return true;
} // end NormStreamer::OpenNormSession() } // end NormStreamer::OpenNormSession()
@ -1338,6 +1344,7 @@ void PrintHelp()
" interface <name> -- Specifies the name of the network interface on which to conduct NORM protocol\n" " interface <name> -- Specifies the name of the network interface on which to conduct NORM protocol\n"
" (e.g., 'eth0')\n" " (e.g., 'eth0')\n"
" loopback -- Enables 'loopback' sessions on the same host machine. Required for multicast loopback.\n" " loopback -- Enables 'loopback' sessions on the same host machine. Required for multicast loopback.\n"
" ptos <value> -- Set special IP traffic class (TOS) for GRTT probing and acknowledgments\n"
" info -- Limits FTI header extension to NORM_INFO message only (reduced overhead)\n" " info -- Limits FTI header extension to NORM_INFO message only (reduced overhead)\n"
" rate <bitsPerSecond> -- sets fixed sender rate (and receiver token bucket rate if 'limit' option is used)\n" " rate <bitsPerSecond> -- sets fixed sender rate (and receiver token bucket rate if 'limit' option is used)\n"
" [cc|cce|ccl] -- Enables optional NORM congestion control mode (overrides 'rate')\n" " [cc|cce|ccl] -- Enables optional NORM congestion control mode (overrides 'rate')\n"
@ -1441,6 +1448,27 @@ int main(int argc, char* argv[])
{ {
loopback = true; loopback = true;
} }
else if (0 == strncmp(cmd, "ptos", len))
{
if (i >= argc)
{
fprintf(stderr, "normStreamer error: missing 'ptos' value!\n");
Usage();
return -1;
}
int tos = -1;
int result = sscanf(argv[i], "%i", &tos);
if (1 != result)
result = sscanf(argv[i], "%x", &tos);
if ((1 != result) || (tos < 0) || (tos > 255))
{
fprintf(stderr, "normStreamer error: invalid 'ptos' value!\n");
Usage();
return -1;
}
i++;
normStreamer.SetProbeTOS(tos);
}
else if (0 == strncmp(cmd, "info", len)) else if (0 == strncmp(cmd, "info", len))
{ {
ftiInfo = true; ftiInfo = true;

View File

@ -515,6 +515,10 @@ void NormSetGrttProbingInterval(NormSessionHandle sessionHandle,
double intervalMin, double intervalMin,
double intervalMax); double intervalMax);
NORM_API_LINKAGE
void NormSetGrttProbingTOS(NormSessionHandle sessionHandle,
UINT8 probeTOS);
NORM_API_LINKAGE NORM_API_LINKAGE
void NormSetBackoffFactor(NormSessionHandle sessionHandle, void NormSetBackoffFactor(NormSessionHandle sessionHandle,
double backoffFactor); double backoffFactor);

View File

@ -656,6 +656,7 @@ class NormMsg
// To retrieve any attached header extensions // To retrieve any attached header extensions
bool HasExtensions() const {return (header_length > header_length_base);} bool HasExtensions() const {return (header_length > header_length_base);}
bool HasExtension(NormHeaderExtension::Type extType);
bool GetNextExtension(NormHeaderExtension& ext) const bool GetNextExtension(NormHeaderExtension& ext) const
{ {
const UINT32* currentBuffer = ext.GetBuffer(); const UINT32* currentBuffer = ext.GetBuffer();
@ -1989,7 +1990,7 @@ class NormNackMsg : public NormMsg
}; };
}; // end class NormNackMsg }; // end class NormNackMsg
class NormAckMsg : public NormMsg class NormAckMsg : public NormAck, public NormMsg
{ {
public: public:

View File

@ -52,10 +52,7 @@ class NormObject
// This must be reset after each update // This must be reset after each update
void SetNotifyOnUpdate(bool state) void SetNotifyOnUpdate(bool state)
{ {notify_on_update = state;}
TRACE("enter SetNotifyOnUpdate(%d) ...\n", state);
notify_on_update = state;
}
// Object information // Object information
NormObject::Type GetType() const {return type;} NormObject::Type GetType() const {return type;}

View File

@ -289,6 +289,12 @@ class NormSession
if (state) probe_proactive = true; if (state) probe_proactive = true;
} }
// This MUST be called before
void SetProbeTOS(UINT8 probeTOS)
{probe_tos = probeTOS;}
UINT8 GetProbeTOS() const
{return probe_tos;}
// This method enables/disables flow control operation. // This method enables/disables flow control operation.
void SetFlowControl(double flowControlFactor) void SetFlowControl(double flowControlFactor)
{flow_control_factor = flowControlFactor;} {flow_control_factor = flowControlFactor;}
@ -674,10 +680,15 @@ class NormSession
void TxSocketRecvHandler(ProtoSocket& theSocket, ProtoSocket::Event theEvent); void TxSocketRecvHandler(ProtoSocket& theSocket, ProtoSocket::Event theEvent);
void RxSocketRecvHandler(ProtoSocket& theSocket, ProtoSocket::Event theEvent); void RxSocketRecvHandler(ProtoSocket& theSocket, ProtoSocket::Event theEvent);
void HandleReceiveMessage(NormMsg& msg, bool wasUnicast, bool ecn = false); void HandleReceiveMessage(NormMsg& msg, bool wasUnicast, bool ecn = false);
#ifdef ECN_SUPPORT
// This is used when raw packet capture is enabled // This is used when raw packet capture is enabled
bool OpenProtoCap();
void CloseProtoCap();
bool RawSendTo(const char* buffer, unsigned int& numBytes, const ProtoAddress& dstAddr, UINT8 trafficClass);
void OnPktCapture(ProtoChannel& theChannel, void OnPktCapture(ProtoChannel& theChannel,
ProtoChannel::Notification notifyType); ProtoChannel::Notification notifyType);
#endif // ECN_SUPPORT
// Sender message handling routines // Sender message handling routines
void SenderHandleNackMessage(const struct timeval& currentTime, void SenderHandleNackMessage(const struct timeval& currentTime,
@ -723,7 +734,10 @@ class NormSession
ProtoSocket tx_socket_actual; ProtoSocket tx_socket_actual;
ProtoSocket* tx_socket; ProtoSocket* tx_socket;
ProtoSocket rx_socket; ProtoSocket rx_socket;
ProtoCap* rx_cap; // raw packet capture alternative to "rx_socket" #ifdef ECN_SUPPORT
ProtoCap* proto_cap; // raw packet capture alternative to "rx_socket"
ProtoAddress src_addr; // used for raw packet sendto()
#endif // ECN_SUPPORT
bool rx_port_reuse; // enable rx_socket port (sessionPort) reuse when true bool rx_port_reuse; // enable rx_socket port (sessionPort) reuse when true
ProtoAddress rx_bind_addr; ProtoAddress rx_bind_addr;
ProtoAddress rx_connect_addr; ProtoAddress rx_connect_addr;
@ -817,6 +831,7 @@ class NormSession
bool probe_reset; bool probe_reset;
bool probe_data_check; // refrain cc probe until data is send bool probe_data_check; // refrain cc probe until data is send
struct timeval probe_time_last; struct timeval probe_time_last;
UINT8 probe_tos; // optionally use different IP TOS for GRTT probe/response
double grtt_interval; // current GRTT update interval double grtt_interval; // current GRTT update interval
double grtt_interval_min; // minimum GRTT update interval double grtt_interval_min; // minimum GRTT update interval

View File

@ -1757,6 +1757,19 @@ void NormSetGrttProbingInterval(NormSessionHandle sessionHandle,
} }
} // end NormSetGrttProbingInterval() } // end NormSetGrttProbingInterval()
NORM_API_LINKAGE
void NormSetGrttProbingTOS(NormSessionHandle sessionHandle,
UINT8 probeTOS)
{
NormInstance* instance = NormInstance::GetInstanceFromSession(sessionHandle);
if (instance && instance->dispatcher.SuspendThread())
{
NormSession* session = (NormSession*)sessionHandle;
session->SetProbeTOS(probeTOS);
instance->dispatcher.ResumeThread();
}
} // end NormSetGrttProbingTOS()
NORM_API_LINKAGE NORM_API_LINKAGE
void NormSetBackoffFactor(NormSessionHandle sessionHandle, void NormSetBackoffFactor(NormSessionHandle sessionHandle,
double backoffFactor) double backoffFactor)

View File

@ -193,7 +193,7 @@ void NormFile::Unlock()
#ifdef HAVE_FLOCK #ifdef HAVE_FLOCK
if (0 != flock(fd, LOCK_UN)) if (0 != flock(fd, LOCK_UN))
{ {
PLOG(PL_ERROR, "NormFile::Unlock() flock() error: %s\n", GetErrorString()); PLOG(PL_ERROR, "NormFile::Unlock() flock(%d) error: %s\n", fd, GetErrorString());
} }
#else #else
#ifdef HAVE_LOCKF #ifdef HAVE_LOCKF

View File

@ -98,6 +98,18 @@ void NormMsg::Display() const
PLOG(PL_ALWAYS, "%02x", *ptr++); PLOG(PL_ALWAYS, "%02x", *ptr++);
} // end NormMsg::Display() } // end NormMsg::Display()
bool NormMsg::HasExtension(NormHeaderExtension::Type extType)
{
NormHeaderExtension ext;
while (GetNextExtension(ext))
{
if (ext.GetType() == extType)
return true;
}
return false;
} // end NormMsg::HasExtension()
bool NormCmdCCMsg::GetCCNode(NormNodeId nodeId, bool NormCmdCCMsg::GetCCNode(NormNodeId nodeId,
UINT8& flags, UINT8& flags,
UINT8& rtt, UINT8& rtt,

View File

@ -2315,7 +2315,8 @@ bool NormFileObject::Open(const char* thePath,
{ {
if (file.Open(thePath, O_RDWR | O_CREAT | O_TRUNC)) if (file.Open(thePath, O_RDWR | O_CREAT | O_TRUNC))
{ {
file.Lock(); if (!file.Lock())
PLOG(PL_WARN, "NormFileObject::Open() warning: NormFile::Lock() failure\n");
} }
else else
{ {
@ -2392,10 +2393,13 @@ bool NormFileObject::Accept(const char* thePath)
void NormFileObject::Close() void NormFileObject::Close()
{ {
if (file.IsOpen())
{
if (NULL != sender) // we've been receiving this file
file.Unlock();
file.Close();
}
NormObject::Close(); NormObject::Close();
if (NULL != sender) // we've been receiving this file
file.Unlock();
file.Close();
} // end NormFileObject::Close() } // end NormFileObject::Close()
bool NormFileObject::WriteSegment(NormBlockId blockId, bool NormFileObject::WriteSegment(NormBlockId blockId,

View File

@ -8,6 +8,7 @@
#include "protoPktETH.h" #include "protoPktETH.h"
#include "protoPktIP.h" #include "protoPktIP.h"
#include "protoNet.h"
const UINT8 NormSession::DEFAULT_TTL = 255; const UINT8 NormSession::DEFAULT_TTL = 255;
const double NormSession::DEFAULT_TRANSMIT_RATE = 64000.0; // bits/sec const double NormSession::DEFAULT_TRANSMIT_RATE = 64000.0; // bits/sec
@ -44,7 +45,7 @@ enum NormSocketCommand
NormSession::NormSession(NormSessionMgr &sessionMgr, NormNodeId localNodeId) NormSession::NormSession(NormSessionMgr &sessionMgr, NormNodeId localNodeId)
: session_mgr(sessionMgr), notify_pending(false), tx_port(0), tx_port_reuse(false), : session_mgr(sessionMgr), notify_pending(false), tx_port(0), tx_port_reuse(false),
tx_socket_actual(ProtoSocket::UDP), tx_socket(&tx_socket_actual), tx_socket_actual(ProtoSocket::UDP), tx_socket(&tx_socket_actual),
rx_socket(ProtoSocket::UDP), rx_cap(NULL), rx_port_reuse(false), local_node_id(localNodeId), rx_socket(ProtoSocket::UDP), proto_cap(NULL), rx_port_reuse(false), local_node_id(localNodeId),
ttl(DEFAULT_TTL), tos(0), loopback(false), mcast_loopback(false), fragmentation(false), ecn_enabled(false), ttl(DEFAULT_TTL), tos(0), loopback(false), mcast_loopback(false), fragmentation(false), ecn_enabled(false),
tx_rate(DEFAULT_TRANSMIT_RATE / 8.0), tx_rate_min(-1.0), tx_rate_max(-1.0), tx_residual(0), tx_rate(DEFAULT_TRANSMIT_RATE / 8.0), tx_rate_min(-1.0), tx_rate_max(-1.0), tx_residual(0),
backoff_factor(DEFAULT_BACKOFF_FACTOR), is_sender(false), backoff_factor(DEFAULT_BACKOFF_FACTOR), is_sender(false),
@ -60,8 +61,7 @@ NormSession::NormSession(NormSessionMgr &sessionMgr, NormNodeId localNodeId)
tx_repair_pending(false), advertise_repairs(false), tx_repair_pending(false), advertise_repairs(false),
suppress_nonconfirmed(false), suppress_rate(-1.0), suppress_rtt(-1.0), suppress_nonconfirmed(false), suppress_rate(-1.0), suppress_rtt(-1.0),
probe_proactive(true), probe_pending(false), probe_reset(true), probe_data_check(false), probe_proactive(true), probe_pending(false), probe_reset(true), probe_data_check(false),
grtt_interval(0.5), probe_tos(0), grtt_interval(0.5), grtt_interval_min(DEFAULT_GRTT_INTERVAL_MIN),
grtt_interval_min(DEFAULT_GRTT_INTERVAL_MIN),
grtt_interval_max(DEFAULT_GRTT_INTERVAL_MAX), grtt_interval_max(DEFAULT_GRTT_INTERVAL_MAX),
grtt_max(DEFAULT_GRTT_MAX), grtt_max(DEFAULT_GRTT_MAX),
grtt_decrease_delay_count(DEFAULT_GRTT_DECREASE_DELAY), grtt_decrease_delay_count(DEFAULT_GRTT_DECREASE_DELAY),
@ -189,6 +189,8 @@ bool NormSession::Open()
{ {
tx_socket = &rx_socket; tx_socket = &rx_socket;
} }
tx_port = tx_socket->GetPort();
} }
if (!rx_socket.IsOpen() && (!tx_only || (&rx_socket == tx_socket))) if (!rx_socket.IsOpen() && (!tx_only || (&rx_socket == tx_socket)))
{ {
@ -298,61 +300,23 @@ bool NormSession::Open()
#ifdef ECN_SUPPORT #ifdef ECN_SUPPORT
// TBD - do this via UDP socket recvmsg() instead of raw packet capture // TBD - do this via UDP socket recvmsg() instead of raw packet capture
// If raw packet capture is enabled, create/open ProtoCap device to do it // If raw packet capture is enabled, create/open ProtoCap device to do it
if (ecn_enabled && !tx_only) if ((ecn_enabled && !tx_only) || (0 != probe_tos))
{ {
if (NULL == rx_cap) if (!OpenProtoCap())
{ {
if (NULL == (rx_cap = ProtoCap::Create())) PLOG(PL_FATAL, "NormSession::Open() error: unable to create ProtoCap device!\n");
{ Close();
PLOG(PL_FATAL, "NormSession::Open() error: unable to create ProtoCap device!\n"); return false;
Close(); }
return false; if (ecn_enabled)
} {
rx_cap->SetListener(this, &NormSession::OnPktCapture); rx_socket.StopInputNotification(); // Disable rx_socket (keep open so mcast JOIN holds)
rx_cap->SetNotifier(session_mgr.GetChannelNotifier()); proto_cap->StartInputNotification();
if (!rx_cap->Open(('\0' != interface_name[0]) ? interface_name : NULL)) }
{ else
PLOG(PL_FATAL, "NormSession::Open() error: unable to open ProtoCap device '%s'!\n", (('\0' != interface_name[0]) ? interface_name : "(null)")); {
Close(); proto_cap->StopInputNotification();
return false;
}
rx_cap->StartInputNotification();
// Populate "dst_addr_list" with potential valid dst addrs for this host
dst_addr_list.Destroy();
if (rx_bind_addr.IsValid())
{
if (!dst_addr_list.Insert(rx_bind_addr))
{
PLOG(PL_FATAL, "NormSession::Open() error: unable to add rx_bind_addr to dst_addr_list!!\n");
Close();
return false;
}
}
else
{
// Put all local unicast addrs in list
if (!ProtoSocket::GetHostAddressList(ProtoAddress::IPv4, dst_addr_list))
PLOG(PL_WARN, "NormSession::Open() warning: incomplete IPv4 host address list\n");
if (!ProtoSocket::GetHostAddressList(ProtoAddress::IPv6, dst_addr_list))
PLOG(PL_WARN, "NormSession::Open() warning: incomplete IPv6 host address list\n");
}
if (address.IsMulticast() && !address.HostIsEqual(rx_bind_addr))
{
if (!dst_addr_list.Insert(address))
{
PLOG(PL_FATAL, "NormSession::Open() error: unable to add session addr to dst_addr_list!!\n");
Close();
return false;
}
}
if (dst_addr_list.IsEmpty())
{
PLOG(PL_FATAL, "NormSession::Open() error: unable to add any addresses to dst_addr_list!!\n");
Close();
return false;
}
} }
rx_socket.StopInputNotification(); // Disable rx_socket (keep open so mcast JOIN holds)
} }
#endif // ECN_SUPPORT #endif // ECN_SUPPORT
if (message_pool.IsEmpty()) if (message_pool.IsEmpty())
@ -402,15 +366,85 @@ void NormSession::Close()
rx_socket.Close(); rx_socket.Close();
} }
#ifdef ECN_SUPPORT #ifdef ECN_SUPPORT
if (NULL != rx_cap) CloseProtoCap();
{
rx_cap->Close();
delete rx_cap;
rx_cap = NULL;
}
#endif // ECN_SUPPORT #endif // ECN_SUPPORT
} // end NormSession::Close() } // end NormSession::Close()
#ifdef ECN_SUPPORT
bool NormSession::OpenProtoCap()
{
if (NULL == proto_cap)
{
if (NULL == (proto_cap = ProtoCap::Create()))
{
PLOG(PL_FATAL, "NormSession::OpenProtoCap() error: unable to create ProtoCap device!\n");
return false;
}
proto_cap->SetListener(this, &NormSession::OnPktCapture);
proto_cap->SetNotifier(session_mgr.GetChannelNotifier());
if (!proto_cap->Open(('\0' != interface_name[0]) ? interface_name : NULL))
{
PLOG(PL_FATAL, "NormSession::OpenProtoCap() error: unable to open ProtoCap device '%s'!\n", (('\0' != interface_name[0]) ? interface_name : "(null)"));
return false;
}
// Populate "dst_addr_list" with potential valid dst addrs for this host
dst_addr_list.Destroy();
if (rx_bind_addr.IsValid())
{
if (!dst_addr_list.Insert(rx_bind_addr))
{
PLOG(PL_FATAL, "NormSession::OpenProtoCap() error: unable to add rx_bind_addr to dst_addr_list!!\n");
return false;
}
}
else
{
// Put all local unicast addrs in list
if (!ProtoSocket::GetHostAddressList(ProtoAddress::IPv4, dst_addr_list))
PLOG(PL_WARN, "NormSession::OpenProtoCap() warning: incomplete IPv4 host address list\n");
if (!ProtoSocket::GetHostAddressList(ProtoAddress::IPv6, dst_addr_list))
PLOG(PL_WARN, "NormSession::OpenProtoCap() warning: incomplete IPv6 host address list\n");
}
if (address.IsMulticast() && !address.HostIsEqual(rx_bind_addr))
{
if (!dst_addr_list.Insert(address))
{
PLOG(PL_FATAL, "NormSession::OpenProtoCap() error: unable to add session addr to dst_addr_list!!\n");
return false;
}
}
// Get the interface source address for pcap message transmission
if (!ProtoNet::GetInterfaceAddress(proto_cap->GetInterfaceIndex(),
address.GetType(),
src_addr))
{
PLOG(PL_WARN, "NormSession::OpenProtoCap() warning: unable to get interface source address\n");
}
TRACE("got source address: %s\n", src_addr.GetHostString());
if (dst_addr_list.IsEmpty())
{
PLOG(PL_FATAL, "NormSession::OpenProtoCap() error: unable to add any addresses to dst_addr_list!!\n");
return false;
}
}
return true;
} // end NormSession::OpenProtoCap()
void NormSession::CloseProtoCap()
{
if (NULL != proto_cap)
{
proto_cap->Close();
delete proto_cap;
proto_cap = NULL;
}
} // end NormSession::CloseProtoCap()
#endif // ECN_SUPPORT
bool NormSession::SetMulticastInterface(const char *interfaceName) bool NormSession::SetMulticastInterface(const char *interfaceName)
{ {
if (NULL != interfaceName) if (NULL != interfaceName)
@ -548,12 +582,8 @@ void NormSession::SetTxOnly(bool txOnly, bool connectToSessionAddress)
if (rx_socket.IsOpen()) if (rx_socket.IsOpen())
rx_socket.Close(); rx_socket.Close();
#ifdef ECN_SUPPORT #ifdef ECN_SUPPORT
if (NULL != rx_cap) if (NULL != proto_cap)
{ proto_cap->StopInputNotification();
rx_cap->Close();
delete rx_cap;
rx_cap = NULL;
}
#endif // ECN_SUPPORT #endif // ECN_SUPPORT
} }
// We connect tx_only session sockets when tx port // We connect tx_only session sockets when tx port
@ -2408,6 +2438,7 @@ void NormSession::RxSocketRecvHandler(ProtoSocket &theSocket,
} // end if/else (theEvent == RECV/SEND) } // end if/else (theEvent == RECV/SEND)
} // end NormSession::RxSocketRecvHandler() } // end NormSession::RxSocketRecvHandler()
#ifdef ECN_SUPPORT
#ifndef SIMULATE #ifndef SIMULATE
void NormSession::OnPktCapture(ProtoChannel &theChannel, void NormSession::OnPktCapture(ProtoChannel &theChannel,
ProtoChannel::Notification notifyType) ProtoChannel::Notification notifyType)
@ -2539,6 +2570,7 @@ void NormSession::OnPktCapture(ProtoChannel &theChannel,
} // end while(1) } // end while(1)
} // end NormSession::OnPktCapture() } // end NormSession::OnPktCapture()
#endif // !SIMULATE #endif // !SIMULATE
#endif // ECN_SUPPORT
// TBD - move this to its own cpp file??? // TBD - move this to its own cpp file???
void NormTrace(const struct timeval &currentTime, void NormTrace(const struct timeval &currentTime,
@ -4841,6 +4873,7 @@ NormSession::MessageStatus NormSession::SendMessage(NormMsg &msg)
//TRACE("sending message length %hu\n", msg.GetLength()); //TRACE("sending message length %hu\n", msg.GetLength());
bool isReceiverMsg = false; bool isReceiverMsg = false;
bool isProbe = false; bool isProbe = false;
bool sendRaw = false;
// Fill in any last minute timestamps // Fill in any last minute timestamps
// (TBD) fill in InstanceId fields on all messages as needed // (TBD) fill in InstanceId fields on all messages as needed
@ -4849,80 +4882,98 @@ NormSession::MessageStatus NormSession::SendMessage(NormMsg &msg)
UINT16 instId = instance_id; // assume it's a sender message (will be overridden otherwise) UINT16 instId = instance_id; // assume it's a sender message (will be overridden otherwise)
switch (msg.GetType()) switch (msg.GetType())
{ {
case NormMsg::INFO: case NormMsg::INFO:
case NormMsg::DATA: case NormMsg::DATA:
{
NormObjectMsg &objMsg = static_cast<NormObjectMsg &>(msg);
objMsg.SetInstanceId(instId);
msg.SetSequence(tx_sequence++); // (TBD) set for session dst msgs
if (syn_status)
objMsg.SetFlag(NormObjectMsg::FLAG_SYN);
break;
}
case NormMsg::CMD:
{
NormCmdMsg &cmd = static_cast<NormCmdMsg &>(msg);
((NormCmdMsg &)msg).SetInstanceId(instId);
switch (cmd.GetFlavor())
{ {
case NormCmdMsg::CC: NormObjectMsg &objMsg = static_cast<NormObjectMsg &>(msg);
{ objMsg.SetInstanceId(instId);
NormCmdCCMsg &ccMsg = static_cast<NormCmdCCMsg &>(cmd); msg.SetSequence(tx_sequence++); // (TBD) set for session dst msgs
struct timeval currentTime;
ProtoSystemTime(currentTime);
ccMsg.SetSendTime(currentTime);
isProbe = true;
if (syn_status) if (syn_status)
ccMsg.SetSyn(); objMsg.SetFlag(NormObjectMsg::FLAG_SYN);
break; break;
} }
case NormCmdMsg::SQUELCH: case NormMsg::CMD:
{
NormCmdMsg &cmd = static_cast<NormCmdMsg &>(msg);
((NormCmdMsg &)msg).SetInstanceId(instId);
switch (cmd.GetFlavor())
{
case NormCmdMsg::CC:
{
NormCmdCCMsg &ccMsg = static_cast<NormCmdCCMsg &>(cmd);
struct timeval currentTime;
ProtoSystemTime(currentTime);
ccMsg.SetSendTime(currentTime);
isProbe = true;
if (0 != probe_tos)
sendRaw = true; // so probe will be marked accordingly
if (syn_status)
ccMsg.SetSyn();
break;
}
case NormCmdMsg::SQUELCH:
break;
default:
break;
}
msg.SetSequence(tx_sequence++); // (TBD) set for session dst msgs
break; break;
}
case NormMsg::NACK:
{
msg.SetSequence(0); // TBD - set per destination
isReceiverMsg = true;
NormNackMsg &nack = (NormNackMsg &)msg;
NormSenderNode *theSender =
(NormSenderNode *)sender_tree.FindNodeById(nack.GetSenderId());
ASSERT(NULL != theSender);
fecM = theSender->GetFecFieldSize();
instId = theSender->GetInstanceId();
struct timeval grttResponse;
// When probe_tos is non-zero, GRTT feedback is in ACKs only
if (0 == probe_tos)
{
struct timeval currentTime;
ProtoSystemTime(currentTime);
theSender->CalculateGrttResponse(currentTime, grttResponse);
}
else
{
grttResponse.tv_sec = grttResponse.tv_usec = 0;
}
nack.SetGrttResponse(grttResponse);
break;
}
case NormMsg::ACK:
{
msg.SetSequence(0); // TBD - set per destination
isReceiverMsg = true;
NormAckMsg &ack = (NormAckMsg &)msg;
NormSenderNode *theSender;
if (IsServerListener())
theSender = client_tree.FindNodeByAddress(ack.GetDestination());
else
theSender = (NormSenderNode *)sender_tree.FindNodeById(ack.GetSenderId());
ASSERT(NULL != theSender);
fecM = theSender->GetFecFieldSize();
instId = theSender->GetInstanceId();
struct timeval grttResponse;
if ((0 == probe_tos) || (NormAck::CC == ack.GetAckType()))
{
struct timeval currentTime;
ProtoSystemTime(currentTime);
theSender->CalculateGrttResponse(currentTime, grttResponse);
ack.SetGrttResponse(grttResponse);
if (0 != probe_tos) sendRaw = true;
}
else
{
grttResponse.tv_sec = grttResponse.tv_usec = 0;
}
break;
}
default: default:
break; break;
}
msg.SetSequence(tx_sequence++); // (TBD) set for session dst msgs
break;
}
case NormMsg::NACK:
{
msg.SetSequence(0); // TBD - set per destination
isReceiverMsg = true;
NormNackMsg &nack = (NormNackMsg &)msg;
NormSenderNode *theSender =
(NormSenderNode *)sender_tree.FindNodeById(nack.GetSenderId());
ASSERT(NULL != theSender);
fecM = theSender->GetFecFieldSize();
instId = theSender->GetInstanceId();
struct timeval currentTime;
ProtoSystemTime(currentTime);
struct timeval grttResponse;
theSender->CalculateGrttResponse(currentTime, grttResponse);
nack.SetGrttResponse(grttResponse);
break;
}
case NormMsg::ACK:
{
msg.SetSequence(0); // TBD - set per destination
isReceiverMsg = true;
NormAckMsg &ack = (NormAckMsg &)msg;
NormSenderNode *theSender;
if (IsServerListener())
theSender = client_tree.FindNodeByAddress(ack.GetDestination());
else
theSender = (NormSenderNode *)sender_tree.FindNodeById(ack.GetSenderId());
ASSERT(NULL != theSender);
fecM = theSender->GetFecFieldSize();
instId = theSender->GetInstanceId();
struct timeval grttResponse;
struct timeval currentTime;
ProtoSystemTime(currentTime);
theSender->CalculateGrttResponse(currentTime, grttResponse);
ack.SetGrttResponse(grttResponse);
break;
}
default:
break;
} }
// Fill in common message fields // Fill in common message fields
msg.SetSourceId(local_node_id); msg.SetSourceId(local_node_id);
@ -4957,8 +5008,11 @@ NormSession::MessageStatus NormSession::SendMessage(NormMsg &msg)
else else
{ {
unsigned int numBytes = msgSize; unsigned int numBytes = msgSize;
bool result = tx_socket->SendTo(msg.GetBuffer(), numBytes, msg.GetDestination()); bool result;
if (sendRaw)
result = RawSendTo(msg.GetBuffer(), numBytes, msg.GetDestination(), probe_tos);
else
result = tx_socket->SendTo(msg.GetBuffer(), numBytes, msg.GetDestination());
if (result) if (result)
{ {
if (numBytes == msgSize) if (numBytes == msgSize)
@ -5031,6 +5085,73 @@ NormSession::MessageStatus NormSession::SendMessage(NormMsg &msg)
return MSG_SEND_OK; return MSG_SEND_OK;
} // end NormSession::SendMessage() } // end NormSession::SendMessage()
#ifdef ECN_SUPPORT
bool NormSession::RawSendTo(const char* buffer, unsigned int& numBytes, const ProtoAddress& dstAddr, UINT8 trafficClass)
{
// Send the message via proto_cap instead of UDP socket
// (Used for marking GRTT probing with different traffic class
// to inform lower layer protocols to not delay the messages
// via retransmission or other means).
UINT32 pcapBuffer[8192/4]; // TBD - Is this big enough???
UINT16* ethBuffer = (UINT16*)pcapBuffer + 2; // offset for IP packet alignment
ProtoPktETH ethPkt(ethBuffer, 8192 - 2);
// Ethernet source address will be set by ProtoCap::Forward() method
ProtoAddress etherDst;
if (dstAddr.IsMulticast())
etherDst.GetEthernetMulticastAddress(dstAddr);
else
etherDst = PROTO_ADDR_BROADCAST;
ethPkt.SetDstAddr(etherDst);
if (ecn_enabled)
{
trafficClass |= ((UINT8)ProtoSocket::ECN_ECT0); // set ECT0 bit
trafficClass &= ~((UINT8)ProtoSocket::ECN_ECT1); // clear ECT1 bit
}
switch (dstAddr.GetType())
{
case ProtoAddress::IPv4:
{
ethPkt.SetType(ProtoPktETH::IP);
ProtoPktIPv4 ip4Pkt;
ip4Pkt.InitIntoBuffer(ethPkt.AccessPayload(), ethPkt.GetBufferLength() - ethPkt.GetHeaderLength());
ip4Pkt.SetTOS(trafficClass);
ip4Pkt.SetID((UINT16)rand());
ip4Pkt.SetTTL(ttl);
ip4Pkt.SetProtocol(ProtoPktIP::UDP);
ip4Pkt.SetSrcAddr(src_addr);
ip4Pkt.SetDstAddr(dstAddr);
ProtoPktUDP udpPkt(ip4Pkt.AccessPayload(), ip4Pkt.GetBufferLength() - ip4Pkt.GetHeaderLength());
udpPkt.SetSrcPort(GetTxPort());
udpPkt.SetDstPort(dstAddr.GetPort());
udpPkt.SetPayload(buffer, numBytes);
ip4Pkt.SetPayloadLength(udpPkt.GetLength());
udpPkt.FinalizeChecksum(ip4Pkt);
ethPkt.SetPayloadLength(ip4Pkt.GetLength());
break;
}
case ProtoAddress::IPv6:
ethPkt.SetType(ProtoPktETH::IPv6);
// IPv6 support TBD
return false;
break;
default:
PLOG(PL_ERROR, "NormSession::RawSendTo() error: invalid address type!\n");
return false;
}
unsigned int ethBytes = ethPkt.GetLength();
bool result = proto_cap->Forward((char*)ethPkt.AccessBuffer(), ethBytes);
if (!result)
{
PLOG(PL_WARN, "NormSession::RawSendTo() warning: proto_cap send failure!\n");
if (0 == ethBytes) numBytes = 0;
}
return result;
}
#endif // NormSession::RawSendTo()
void NormSession::SetGrttProbingInterval(double intervalMin, double intervalMax) void NormSession::SetGrttProbingInterval(double intervalMin, double intervalMax)
{ {
if ((intervalMin < 0.0) || (intervalMax < 0.0)) if ((intervalMin < 0.0) || (intervalMax < 0.0))