CS144Lab/libsponge/tcp_helpers/tcp_over_ip.cc
2021-10-19 18:08:12 -07:00

91 lines
3.4 KiB
C++

#include "tcp_over_ip.hh"
#include "ipv4_datagram.hh"
#include "ipv4_header.hh"
#include "parser.hh"
#include <arpa/inet.h>
#include <stdexcept>
#include <unistd.h>
#include <utility>
using namespace std;
//! \details This function attempts to parse a TCP segment from
//! the IP datagram's payload.
//!
//! If this succeeds, it then checks that the received segment is related to the
//! current connection. When a TCP connection has been established, this means
//! checking that the source and destination ports in the TCP header are correct.
//!
//! If the TCP connection is listening (i.e., TCPOverIPv4OverTunFdAdapter::_listen is `true`)
//! and the TCP segment read from the wire includes a SYN, this function clears the
//! `_listen` flag and records the source and destination addresses and port numbers
//! from the TCP header; it uses this information to filter future reads.
//! \returns a std::optional<TCPSegment> that is empty if the segment was invalid or unrelated
optional<TCPSegment> TCPOverIPv4Adapter::unwrap_tcp_in_ip(const InternetDatagram &ip_dgram) {
// is the IPv4 datagram for us?
// Note: it's valid to bind to address "0" (INADDR_ANY) and reply from actual address contacted
if (not listening() and (ip_dgram.header().dst != config().source.ipv4_numeric())) {
return {};
}
// is the IPv4 datagram from our peer?
if (not listening() and (ip_dgram.header().src != config().destination.ipv4_numeric())) {
return {};
}
// does the IPv4 datagram claim that its payload is a TCP segment?
if (ip_dgram.header().proto != IPv4Header::PROTO_TCP) {
return {};
}
// is the payload a valid TCP segment?
TCPSegment tcp_seg;
if (ParseResult::NoError != tcp_seg.parse(ip_dgram.payload(), ip_dgram.header().pseudo_cksum())) {
return {};
}
// is the TCP segment for us?
if (tcp_seg.header().dport != config().source.port()) {
return {};
}
// should we target this source addr/port (and use its destination addr as our source) in reply?
if (listening()) {
if (tcp_seg.header().syn and not tcp_seg.header().rst) {
config_mutable().source = {inet_ntoa({htobe32(ip_dgram.header().dst)}), config().source.port()};
config_mutable().destination = {inet_ntoa({htobe32(ip_dgram.header().src)}), tcp_seg.header().sport};
set_listening(false);
} else {
return {};
}
}
// is the TCP segment from our peer?
if (tcp_seg.header().sport != config().destination.port()) {
return {};
}
return tcp_seg;
}
//! Takes a TCP segment, sets port numbers as necessary, and wraps it in an IPv4 datagram
//! \param[in] seg is the TCP segment to convert
InternetDatagram TCPOverIPv4Adapter::wrap_tcp_in_ip(TCPSegment &seg) {
// set the port numbers in the TCP segment
seg.header().sport = config().source.port();
seg.header().dport = config().destination.port();
// create an Internet Datagram and set its addresses and length
InternetDatagram ip_dgram;
ip_dgram.header().src = config().source.ipv4_numeric();
ip_dgram.header().dst = config().destination.ipv4_numeric();
ip_dgram.header().len = ip_dgram.header().hlen * 4 + seg.header().doff * 4 + seg.payload().size();
// set payload, calculating TCP checksum using information from IP header
ip_dgram.payload() = seg.serialize(ip_dgram.header().pseudo_cksum());
return ip_dgram;
}