//------------------------------------------------------------------------------
// Author: Pavel Karneliuk
// Description: Hash for network sessions
// Copyright (c) 2014 EPAM Systems
//------------------------------------------------------------------------------
/*
This file is part of Nfstrace.
Nfstrace is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, version 2 of the License.
Nfstrace is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Nfstrace. If not, see .
*/
//------------------------------------------------------------------------------
#ifndef SESSIONS_HASH_H
#define SESSIONS_HASH_H
//------------------------------------------------------------------------------
#include
#include
#include
#include
#include
#include "controller/parameters.h"
#include "filtration/packet.h"
#include "utils/out.h"
#include "utils/sessions.h"
//------------------------------------------------------------------------------
namespace NST
{
namespace filtration
{
struct MapperImpl
{
using Session = NST::utils::Session;
using NetworkSession = NST::utils::NetworkSession;
MapperImpl() = delete;
static inline Session::Direction ipv4_direction(const Session& key)
{
if(key.port[0] < key.port[1])
return Session::Source;
else if(key.port[0] > key.port[1])
return Session::Destination;
// Ok, ports are equal, compare addresses
return (key.ip.v4.addr[0] < key.ip.v4.addr[1]) ? Session::Source : Session::Destination;
}
struct IPv4PortsKeyHash
{
inline std::size_t operator()(const Session& key) const
{
return key.port[0] +
key.port[1] +
key.ip.v4.addr[0] +
key.ip.v4.addr[1];
}
};
struct IPv4PortsKeyEqual
{
inline bool operator()(const Session& a, const Session& b) const
{
if((a.port[0] == b.port[0]) &&
(a.port[1] == b.port[1]) &&
(a.ip.v4.addr[0] == b.ip.v4.addr[0]) &&
(a.ip.v4.addr[1] == b.ip.v4.addr[1]))
return true;
if((a.port[1] == b.port[0]) &&
(a.port[0] == b.port[1]) &&
(a.ip.v4.addr[1] == b.ip.v4.addr[0]) &&
(a.ip.v4.addr[0] == b.ip.v4.addr[1]))
return true;
return false;
}
};
static inline Session::Direction ipv6_direction(const Session& key)
{
if(key.port[0] < key.port[1])
return Session::Source;
else if(key.port[0] > key.port[1])
return Session::Destination;
// Ok, ports are equal, compare addresses
const uint32_t* s{key.ip.v6.addr_uint32[0]};
const uint32_t* d{key.ip.v6.addr_uint32[1]};
if(s[0] != d[0]) return (s[0] < d[0]) ? Session::Source : Session::Destination;
if(s[1] != d[1]) return (s[1] < d[1]) ? Session::Source : Session::Destination;
if(s[2] != d[2]) return (s[2] < d[2]) ? Session::Source : Session::Destination;
return (s[3] < d[3]) ? Session::Source : Session::Destination;
}
static inline void copy_ipv6(uint32_t dst[4], const uint8_t src[16])
{
uint8_t* d{reinterpret_cast(dst)};
memcpy(d, src, sizeof(uint32_t) * 4);
}
struct IPv6PortsKeyHash
{
std::size_t operator()(const Session& key) const
{
std::size_t ret = key.port[0] + key.port[1];
ret += key.ip.v6.addr_uint32[0][0];
ret += key.ip.v6.addr_uint32[0][1];
ret += key.ip.v6.addr_uint32[0][2];
ret += key.ip.v6.addr_uint32[0][3];
ret += key.ip.v6.addr_uint32[1][0];
ret += key.ip.v6.addr_uint32[1][1];
ret += key.ip.v6.addr_uint32[1][2];
ret += key.ip.v6.addr_uint32[1][3];
return ret;
}
};
struct IPv6PortsKeyEqual
{
static inline bool eq_ipv6_address(const uint32_t a[4], const uint32_t b[4])
{
return a[0] == b[0] &&
a[1] == b[1] &&
a[2] == b[2] &&
a[3] == b[3];
}
bool operator()(const Session& a, const Session& b) const
{
if((a.port[0] == b.port[0]) && (a.port[1] == b.port[1]))
{
if(eq_ipv6_address(a.ip.v6.addr_uint32[0], b.ip.v6.addr_uint32[0]) && eq_ipv6_address(a.ip.v6.addr_uint32[1], b.ip.v6.addr_uint32[1]))
return true;
}
if((a.port[1] == b.port[0]) && (a.port[0] == b.port[1]))
{
if(eq_ipv6_address(a.ip.v6.addr_uint32[1], b.ip.v6.addr_uint32[0]) && eq_ipv6_address(a.ip.v6.addr_uint32[0], b.ip.v6.addr_uint32[1]))
return true;
}
return false;
}
};
};
struct IPv4TCPMapper : private MapperImpl
{
static inline void fill_hash_key(PacketInfo& info, Session& key)
{
key.port[0] = info.tcp->sport();
key.port[1] = info.tcp->dport();
key.ip.v4.addr[0] = info.ipv4->src();
key.ip.v4.addr[1] = info.ipv4->dst();
info.direction = MapperImpl::ipv4_direction(key);
}
static inline void fill_session(const PacketInfo& info, NetworkSession& session)
{
session.ip_type = Session::v4;
session.type = Session::TCP;
session.direction = info.direction;
session.port[0] = info.tcp->sport();
session.port[1] = info.tcp->dport();
session.ip.v4.addr[0] = info.ipv4->src();
session.ip.v4.addr[1] = info.ipv4->dst();
}
using KeyHash = MapperImpl::IPv4PortsKeyHash;
using KeyEqual = MapperImpl::IPv4PortsKeyEqual;
};
struct IPv4UDPMapper : private MapperImpl
{
static inline void fill_hash_key(PacketInfo& info, Session& key)
{
key.port[0] = info.udp->sport();
key.port[1] = info.udp->dport();
key.ip.v4.addr[0] = info.ipv4->src();
key.ip.v4.addr[1] = info.ipv4->dst();
info.direction = MapperImpl::ipv4_direction(key);
}
static inline void fill_session(const PacketInfo& info, NetworkSession& session)
{
session.ip_type = Session::v4;
session.type = Session::UDP;
session.direction = info.direction;
session.port[0] = info.udp->sport();
session.port[1] = info.udp->dport();
session.ip.v4.addr[0] = info.ipv4->src();
session.ip.v4.addr[1] = info.ipv4->dst();
}
using KeyHash = MapperImpl::IPv4PortsKeyHash;
using KeyEqual = MapperImpl::IPv4PortsKeyEqual;
};
struct IPv6TCPMapper : private MapperImpl
{
static inline void fill_hash_key(PacketInfo& info, Session& key)
{
key.port[0] = info.tcp->sport();
key.port[1] = info.tcp->dport();
MapperImpl::copy_ipv6(key.ip.v6.addr_uint32[0], info.ipv6->src());
MapperImpl::copy_ipv6(key.ip.v6.addr_uint32[1], info.ipv6->dst());
info.direction = MapperImpl::ipv6_direction(key);
}
static inline void fill_session(const PacketInfo& info, NetworkSession& session)
{
session.ip_type = Session::v6;
session.type = Session::TCP;
session.direction = info.direction;
session.port[0] = info.tcp->sport();
session.port[1] = info.tcp->dport();
MapperImpl::copy_ipv6(session.ip.v6.addr_uint32[0], info.ipv6->src());
MapperImpl::copy_ipv6(session.ip.v6.addr_uint32[1], info.ipv6->dst());
}
using KeyHash = MapperImpl::IPv6PortsKeyHash;
using KeyEqual = MapperImpl::IPv6PortsKeyEqual;
};
struct IPv6UDPMapper : private MapperImpl
{
static inline void fill_hash_key(PacketInfo& info, Session& key)
{
key.port[0] = info.udp->sport();
key.port[1] = info.udp->dport();
MapperImpl::copy_ipv6(key.ip.v6.addr_uint32[0], info.ipv6->src());
MapperImpl::copy_ipv6(key.ip.v6.addr_uint32[1], info.ipv6->dst());
info.direction = MapperImpl::ipv6_direction(key);
}
static inline void fill_session(const PacketInfo& info, NetworkSession& session)
{
session.ip_type = Session::v6;
session.type = Session::UDP;
session.direction = info.direction;
session.port[0] = info.udp->sport();
session.port[1] = info.udp->dport();
MapperImpl::copy_ipv6(session.ip.v6.addr_uint32[0], info.ipv6->src());
MapperImpl::copy_ipv6(session.ip.v6.addr_uint32[1], info.ipv6->dst());
}
using KeyHash = MapperImpl::IPv6PortsKeyHash;
using KeyEqual = MapperImpl::IPv6PortsKeyEqual;
};
// SessionsHash creates sessions and stores them in hash
template <
typename Mapper, // map PacketInfo& to SessionImpl*
typename SessionImpl, // mapped type
typename Writer>
class SessionsHash
{
public:
static_assert(std::is_convertible::value,
"SessionImpl must be convertible to utils::NetworkSession");
using Container = std::unordered_map;
SessionsHash(Writer* w)
: sessions{}
, writer{w}
, max_hdr{0}
{
max_hdr = controller::Parameters::rpcmsg_limit();
}
~SessionsHash()
{
for(auto& s : sessions)
{
delete s.second;
}
}
void collect_packet(PacketInfo& info)
{
utils::Session key;
Mapper::fill_hash_key(info, key);
auto i = sessions.find(key);
if(i == sessions.end())
{
std::unique_ptr ptr{new SessionImpl{writer, max_hdr}};
auto res = sessions.emplace(key, ptr.get());
if(res.second) // add new - success
{
ptr.release();
i = res.first;
// fill new session after construction
utils::NetworkSession& session = *(res.first->second);
Mapper::fill_session(info, session);
}
}
i->second->collect(info);
}
private:
Container sessions;
Writer* writer;
uint32_t max_hdr;
};
} // namespace filtration
} // namespace NST
//------------------------------------------------------------------------------
#endif // SESSIONS_HASH_H
//------------------------------------------------------------------------------