Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

synthesize tcp reject #1957

Open
wants to merge 2 commits into
base: dev
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions llarp/handlers/tun.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1079,9 +1079,9 @@ namespace llarp
addr = *maybe;
else
{
// send icmp unreachable as we dont have any exits for this ip
if (const auto icmp = pkt.MakeICMPUnreachable())
HandleWriteIPPacket(icmp->ConstBuffer(), dst, src, 0);
// send rejection as we dont have any exits for this ip
if (const auto reply = pkt.MakeReject())
HandleWriteIPPacket(reply->ConstBuffer(), dst, src, 0);

return;
}
Expand Down
132 changes: 93 additions & 39 deletions llarp/net/ip_packet.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -194,6 +194,18 @@ namespace llarp::net
return ExpandV4Lan(srcv4());
}

/// take in a 32 bit int and make sure it's its 16 bit one's complement version
static uint16_t
ipchksum_reduce(uint32_t sum)
{
// only need to do it 2 times to be sure
// proof: 0xFFff + 0xFFff = 0x1FFfe -> 0xFFff
sum = (sum & 0xFFff) + (sum >> 16);
sum += sum >> 16;

return uint16_t((~sum) & 0xFFff);
}

uint16_t
ipchksum(const byte_t* buf, size_t sz, uint32_t sum)
{
Expand All @@ -211,12 +223,7 @@ namespace llarp::net
sum += x;
}

// only need to do it 2 times to be sure
// proof: 0xFFff + 0xFFff = 0x1FFfe -> 0xFFff
sum = (sum & 0xFFff) + (sum >> 16);
sum += sum >> 16;

return uint16_t((~sum) & 0xFFff);
return ipchksum_reduce(sum);
}

#define ADD32CS(x) ((uint32_t)(x & 0xFFff) + (uint32_t)(x >> 16))
Expand Down Expand Up @@ -549,56 +556,103 @@ namespace llarp::net
}

std::optional<IPPacket>
IPPacket::MakeICMPUnreachable() const
IPPacket::MakeReject() const
{
if (IsV4())
// see: rfc1812 4.3.2.7 ( https://www.rfc-editor.org/rfc/rfc1812#section-4.3.2.7 )
{
constexpr auto icmp_Header_size = 8;
constexpr auto ip_Header_size = 20;
net::IPPacket pkt{};
auto* pkt_Header = pkt.Header();

pkt_Header->version = 4;
pkt_Header->ihl = 0x05;
pkt_Header->tos = 0;
pkt_Header->check = 0;
pkt_Header->tot_len = ntohs(icmp_Header_size + ip_Header_size);
pkt_Header->saddr = Header()->daddr;
pkt_Header->daddr = Header()->saddr;
pkt_Header->protocol = 1; // ICMP
pkt_Header->ttl = 1;
pkt_Header->frag_off = htons(0b0100000000000000);
// size pf ip header
const size_t l3_HeaderSize = Header()->ihl * 4;
// size of l4 packet to reflect back
const size_t l4_PacketSize = 8;
pkt_Header->tot_len += ntohs(l4_PacketSize + l3_HeaderSize);
// do not reply to icmp errors
if (Header()->protocol == 1 and buf[Header()->ihl * 4] == 3)
return std::nullopt;

// do not reply to multicast
auto multicast = IPRange::FromIPv4(224, 0, 0, 0, 4);
if (multicast.Contains(dstv4()))
return std::nullopt;
// do not reply to broadcast
uint32_t broadcast = 0xffff'ffff;
if (Header()->daddr == broadcast)
return std::nullopt;
}

// TODO: ipv6
if (not IsV4())
return std::nullopt;

const bool is_tcp = Header()->protocol == 6;

constexpr auto icmp_Header_size = 8;
constexpr auto tcp_Header_size = 20;
net::IPPacket pkt{};
auto* pkt_Header = pkt.Header();

pkt_Header->version = 4;
pkt_Header->ihl = 0x05;
pkt_Header->tos = 0;
pkt_Header->check = 0;
pkt_Header->saddr = Header()->daddr;
pkt_Header->daddr = Header()->saddr;
pkt_Header->ttl = Header()->ttl;
pkt_Header->frag_off = htons(0b0100000000000000);
// use tcp for tcp otherwise imcp
pkt_Header->protocol = is_tcp ? 6 : 1;
// size pf ip header
const size_t l3_HeaderSize = Header()->ihl * 4;
// size of l4 packet to reflect back
const size_t l4_PacketSize = is_tcp ? tcp_Header_size : icmp_Header_size;
const auto pkt_size = l4_PacketSize + l3_HeaderSize;
pkt_Header->tot_len = htons(pkt_size);
pkt.sz = pkt_size;
uint8_t* itr = pkt.buf + (pkt_Header->ihl * 4);
if (is_tcp)
{
// copy tcp segment
std::copy_n(buf + l3_HeaderSize, l4_PacketSize, itr);
// zero tcp checksum
oxenc::write_host_as_big<uint16_t>(0, itr + 16);
// zero data offset and ns bit cleared
itr[12] = 0x00;
// tcp rst flag set
itr[13] = 0x04;
// psuedo header
uint32_t sum{};
uint16_t proto = htons(6);
uint16_t tcplen = htons(tcp_Header_size);
sum = ipchksum((const byte_t*)&pkt_Header->saddr, 4, sum);
sum = ipchksum((const byte_t*)&pkt_Header->daddr, 4, sum);
sum = ipchksum((const byte_t*)&proto, 2, sum);
sum = ipchksum((const byte_t*)&tcplen, 2, sum);
// tcp header
sum = ipchksum(itr, tcp_Header_size, sum);
// put tcp checksum
oxenc::write_host_as_big<uint16_t>(ipchksum_reduce(sum), itr + 16);
}
else
{
uint16_t* checksum;
uint8_t* itr = pkt.buf + (pkt_Header->ihl * 4);
uint8_t* icmp_begin = itr; // type 'destination unreachable'
uint8_t* icmp_begin = itr;
// type 'destination unreachable'
*itr++ = 3;
// code 'Destination host unknown error'
*itr++ = 7;
// code 'host unreachable'
*itr++ = 1;
// checksum + unused
oxenc::write_host_as_big<uint32_t>(0, itr);
checksum = (uint16_t*)itr;
checksum = reinterpret_cast<uint16_t*>(itr);
itr += 4;
// next hop mtu is ignored but let's put something here anyways just in case tm
oxenc::write_host_as_big<uint16_t>(1500, itr);
itr += 2;
// copy ip header and first 8 bytes of datagram for icmp rject

// copy ip header and first 8 bytes of datagram for icmp reject
std::copy_n(buf, l4_PacketSize + l3_HeaderSize, itr);
itr += l4_PacketSize + l3_HeaderSize;
// calculate checksum of ip header
pkt_Header->check = ipchksum(pkt.buf, pkt_Header->ihl * 4);

const auto icmp_size = std::distance(icmp_begin, itr);
// calculate icmp checksum
*checksum = ipchksum(icmp_begin, icmp_size);
pkt.sz = ntohs(pkt_Header->tot_len);
return pkt;
}
return std::nullopt;
// calculate checksum of ip header
pkt.Header()->check = ipchksum(pkt.buf, pkt_Header->ihl * 4);
return pkt;
}

std::optional<std::pair<const char*, size_t>>
Expand Down
6 changes: 4 additions & 2 deletions llarp/net/ip_packet.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -253,9 +253,11 @@ namespace llarp::net
void
ZeroSourceAddress(std::optional<nuint32_t> flowlabel = std::nullopt);

/// make an icmp unreachable reply packet based of this ip packet
/// make an ip packet that will close or reject whatever upper layer sent it
/// makes a tcp rst packet for tcp, otherwise an icmp unreachble packet
/// returns nullopt if not implemented or applicable for this packet
std::optional<IPPacket>
MakeICMPUnreachable() const;
MakeReject() const;
};

/// generate ip checksum
Expand Down
9 changes: 8 additions & 1 deletion llarp/net/sock_addr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,12 @@ namespace llarp
{
return memcmp(&lh, &rh, sizeof(in6_addr)) == 0;
}

bool
operator<(const in6_addr& lh, const in6_addr& rh)
{
return memcmp(&lh, &rh, sizeof(in6_addr)) < 0;
}
/// shared utility functions
///

Expand Down Expand Up @@ -209,7 +215,8 @@ namespace llarp
bool
SockAddr::operator<(const SockAddr& other) const
{
return (m_addr.sin6_addr.s6_addr < other.m_addr.sin6_addr.s6_addr);
return (m_addr.sin6_addr < other.m_addr.sin6_addr)
or (m_addr.sin6_port < other.m_addr.sin6_port);
}

bool
Expand Down