diff --git a/src/Discard.cc b/src/Discard.cc index 6f3d8b94ca..4ccf71f1df 100644 --- a/src/Discard.cc +++ b/src/Discard.cc @@ -34,7 +34,7 @@ bool Discarder::IsActive() return check_ip || check_tcp || check_udp || check_icmp; } -bool Discarder::NextPacket(const std::unique_ptr& ip, int len, int caplen) +bool Discarder::NextPacket(const std::shared_ptr& ip, int len, int caplen) { bool discard_packet = false; diff --git a/src/Discard.h b/src/Discard.h index 79222bd6f8..9f082bdccc 100644 --- a/src/Discard.h +++ b/src/Discard.h @@ -26,7 +26,7 @@ public: bool IsActive(); - bool NextPacket(const std::unique_ptr& ip, int len, int caplen); + bool NextPacket(const std::shared_ptr& ip, int len, int caplen); protected: Val* BuildData(const u_char* data, int hdrlen, int len, int caplen); diff --git a/src/Frag.cc b/src/Frag.cc index 06a940008e..17159fee25 100644 --- a/src/Frag.cc +++ b/src/Frag.cc @@ -31,7 +31,7 @@ void FragTimer::Dispatch(double t, bool /* is_expire */) reporter->InternalWarning("fragment timer dispatched w/o reassembler"); } -FragReassembler::FragReassembler(session::Manager* arg_s, const std::unique_ptr& ip, +FragReassembler::FragReassembler(session::Manager* arg_s, const std::shared_ptr& ip, const u_char* pkt, const FragReassemblerKey& k, double t) : Reassembler(0, REASSEM_FRAG) { @@ -74,7 +74,7 @@ FragReassembler::~FragReassembler() delete[] proto_hdr; } -void FragReassembler::AddFragment(double t, const std::unique_ptr& ip, const u_char* pkt) +void FragReassembler::AddFragment(double t, const std::shared_ptr& ip, const u_char* pkt) { const struct ip* ip4 = ip->IP4_Hdr(); @@ -294,7 +294,7 @@ void FragReassembler::BlockInserted(DataBlockMap::const_iterator /* it */) { struct ip* reassem4 = (struct ip*)pkt_start; reassem4->ip_len = htons(frag_size + proto_hdr_len); - reassembled_pkt = std::make_unique(reassem4, true, true); + reassembled_pkt = std::make_shared(reassem4, true, true); DeleteTimer(); } @@ -303,7 +303,7 @@ void FragReassembler::BlockInserted(DataBlockMap::const_iterator /* it */) struct ip6_hdr* reassem6 = (struct ip6_hdr*)pkt_start; reassem6->ip6_plen = htons(frag_size + proto_hdr_len - 40); const IPv6_Hdr_Chain* chain = new IPv6_Hdr_Chain(reassem6, next_proto, n); - reassembled_pkt = std::make_unique(reassem6, true, n, chain, true); + reassembled_pkt = std::make_shared(reassem6, true, n, chain, true); DeleteTimer(); } @@ -338,7 +338,7 @@ FragmentManager::~FragmentManager() Clear(); } -FragReassembler* FragmentManager::NextFragment(double t, const std::unique_ptr& ip, +FragReassembler* FragmentManager::NextFragment(double t, const std::shared_ptr& ip, const u_char* pkt) { uint32_t frag_id = ip->ID(); diff --git a/src/Frag.h b/src/Frag.h index c76989d418..eff680011c 100644 --- a/src/Frag.h +++ b/src/Frag.h @@ -31,17 +31,17 @@ using FragReassemblerKey = std::tuple; class FragReassembler : public Reassembler { public: - FragReassembler(session::Manager* s, const std::unique_ptr& ip, const u_char* pkt, + FragReassembler(session::Manager* s, const std::shared_ptr& ip, const u_char* pkt, const FragReassemblerKey& k, double t); ~FragReassembler() override; - void AddFragment(double t, const std::unique_ptr& ip, const u_char* pkt); + void AddFragment(double t, const std::shared_ptr& ip, const u_char* pkt); void Expire(double t); void DeleteTimer(); void ClearTimer() { expire_timer = nullptr; } - std::unique_ptr ReassembledPkt() { return std::move(reassembled_pkt); } + std::shared_ptr ReassembledPkt() { return std::move(reassembled_pkt); } const FragReassemblerKey& Key() const { return key; } protected: @@ -50,7 +50,7 @@ protected: void Weird(const char* name) const; u_char* proto_hdr; - std::unique_ptr reassembled_pkt; + std::shared_ptr reassembled_pkt; session::Manager* s; uint64_t frag_size; // size of fully reassembled fragment FragReassemblerKey key; @@ -81,7 +81,7 @@ public: FragmentManager() = default; ~FragmentManager(); - FragReassembler* NextFragment(double t, const std::unique_ptr& ip, const u_char* pkt); + FragReassembler* NextFragment(double t, const std::shared_ptr& ip, const u_char* pkt); void Clear(); void Remove(detail::FragReassembler* f); diff --git a/src/PacketFilter.cc b/src/PacketFilter.cc index b9b156343e..91241eb57f 100644 --- a/src/PacketFilter.cc +++ b/src/PacketFilter.cc @@ -82,7 +82,7 @@ bool PacketFilter::RemoveDst(Val* dst) return f != nullptr; } -bool PacketFilter::Match(const std::unique_ptr& ip, int len, int caplen) +bool PacketFilter::Match(const std::shared_ptr& ip, int len, int caplen) { Filter* f = (Filter*)src_filter.Lookup(ip->SrcAddr(), 128); if ( f ) diff --git a/src/PacketFilter.h b/src/PacketFilter.h index 7c2ba5154e..3470ed0b02 100644 --- a/src/PacketFilter.h +++ b/src/PacketFilter.h @@ -38,7 +38,7 @@ public: bool RemoveDst(Val* dst); // Returns true if packet matches a drop filter - bool Match(const std::unique_ptr& ip, int len, int caplen); + bool Match(const std::shared_ptr& ip, int len, int caplen); private: struct Filter diff --git a/src/TunnelEncapsulation.h b/src/TunnelEncapsulation.h index 960886a1a3..88258e297f 100644 --- a/src/TunnelEncapsulation.h +++ b/src/TunnelEncapsulation.h @@ -7,6 +7,7 @@ #include #include "zeek/ID.h" +#include "zeek/IP.h" #include "zeek/IPAddr.h" #include "zeek/NetVar.h" #include "zeek/UID.h" @@ -66,8 +67,9 @@ public: * Copy constructor. */ EncapsulatingConn(const EncapsulatingConn& other) - : src_addr(other.src_addr), dst_addr(other.dst_addr), src_port(other.src_port), - dst_port(other.dst_port), proto(other.proto), type(other.type), uid(other.uid) + : ip_hdr(other.ip_hdr), src_addr(other.src_addr), dst_addr(other.dst_addr), + src_port(other.src_port), dst_port(other.dst_port), proto(other.proto), type(other.type), + uid(other.uid) { } @@ -87,6 +89,7 @@ public: proto = other.proto; type = other.type; uid = other.uid; + ip_hdr = other.ip_hdr; } return *this; @@ -127,6 +130,9 @@ public: return ! (ec1 == ec2); } + // TODO: temporarily public + std::shared_ptr ip_hdr; + protected: IPAddr src_addr; IPAddr dst_addr; @@ -221,6 +227,28 @@ public: return ! (e1 == e2); } + /** + * Returns a pointer the last element in the stack. Returns a nullptr + * if the stack is empty or hasn't been initialized yet. + */ + EncapsulatingConn* Last() { return Depth() > 0 ? &(conns->back()) : nullptr; } + + /** + * Returns an EncapsulatingConn from the requested index in the stack. + * + * @param index An index to look up. Note this is one-indexed, since it's generally + * looked up using a value from Depth(). + * @return The corresponding EncapsulatingConn, or a nullptr if the requested index is + * out of range. + */ + EncapsulatingConn* At(size_t index) + { + if ( index > 0 && index <= Depth() ) + return &(conns->at(index - 1)); + + return nullptr; + } + protected: std::vector* conns; }; diff --git a/src/analyzer/protocol/ayiya/AYIYA.cc b/src/analyzer/protocol/ayiya/AYIYA.cc index 2523607de5..844c76ebe0 100644 --- a/src/analyzer/protocol/ayiya/AYIYA.cc +++ b/src/analyzer/protocol/ayiya/AYIYA.cc @@ -47,7 +47,7 @@ void AYIYA_Analyzer::DeliverPacket(int len, const u_char* data, bool orig, uint6 caplen -= inner_packet_offset; inner_packet_offset = -1; - std::unique_ptr inner; + std::shared_ptr inner; int result = packet_analysis::IP::ParsePacket(len, data, next_header, inner); if ( result == 0 ) diff --git a/src/analyzer/protocol/geneve/Geneve.cc b/src/analyzer/protocol/geneve/Geneve.cc index 6ad414bf32..92c3570e82 100644 --- a/src/analyzer/protocol/geneve/Geneve.cc +++ b/src/analyzer/protocol/geneve/Geneve.cc @@ -46,6 +46,7 @@ void Geneve_Analyzer::DeliverPacket(int len, const u_char* data, bool orig, uint EncapsulatingConn inner(Conn(), BifEnum::Tunnel::GENEVE); outer->Add(inner); + int encap_index = outer->Depth(); uint8_t tunnel_opt_len = (data[0] & 0x3F) * 4; auto vni = (data[4] << 16) + (data[5] << 8) + (data[6] << 0); @@ -83,8 +84,12 @@ void Geneve_Analyzer::DeliverPacket(int len, const u_char* data, bool orig, uint ProtocolConfirmation(); if ( geneve_packet ) - Conn()->EnqueueEvent(geneve_packet, nullptr, ConnVal(), pkt.ip_hdr->ToPktHdrVal(), - val_mgr->Count(vni)); + { + EncapsulatingConn* ec = pkt.encap->At(encap_index); + if ( ec && ec->ip_hdr ) + Conn()->EnqueueEvent(geneve_packet, nullptr, ConnVal(), pkt.ip_hdr->ToPktHdrVal(), + val_mgr->Count(vni)); + } } } // namespace zeek::analyzer::geneve diff --git a/src/analyzer/protocol/gtpv1/GTPv1.cc b/src/analyzer/protocol/gtpv1/GTPv1.cc index 8677d0c4ab..89a14fea32 100644 --- a/src/analyzer/protocol/gtpv1/GTPv1.cc +++ b/src/analyzer/protocol/gtpv1/GTPv1.cc @@ -48,7 +48,7 @@ void GTPv1_Analyzer::DeliverPacket(int len, const u_char* data, bool orig, uint6 caplen -= inner_packet_offset; inner_packet_offset = -1; - std::unique_ptr inner = nullptr; + std::shared_ptr inner = nullptr; int result = packet_analysis::IP::ParsePacket(len, data, next_header, inner); if ( result == 0 ) diff --git a/src/analyzer/protocol/teredo/Teredo.cc b/src/analyzer/protocol/teredo/Teredo.cc index 5868031243..20787e41e6 100644 --- a/src/analyzer/protocol/teredo/Teredo.cc +++ b/src/analyzer/protocol/teredo/Teredo.cc @@ -94,7 +94,7 @@ bool TeredoEncapsulation::DoParse(const u_char* data, int& len, bool found_origi return false; } -RecordValPtr TeredoEncapsulation::BuildVal(const std::unique_ptr& inner) const +RecordValPtr TeredoEncapsulation::BuildVal(const std::shared_ptr& inner) const { static auto teredo_hdr_type = id::find_type("teredo_hdr"); static auto teredo_auth_type = id::find_type("teredo_auth"); @@ -164,7 +164,7 @@ void Teredo_Analyzer::DeliverPacket(int len, const u_char* data, bool orig, uint return; } - std::unique_ptr inner = nullptr; + std::shared_ptr inner = nullptr; int rslt = packet_analysis::IP::ParsePacket(len, te.InnerIP(), IPPROTO_IPV6, inner); if ( rslt > 0 ) diff --git a/src/analyzer/protocol/teredo/Teredo.h b/src/analyzer/protocol/teredo/Teredo.h index 04f19cefef..a37ad61505 100644 --- a/src/analyzer/protocol/teredo/Teredo.h +++ b/src/analyzer/protocol/teredo/Teredo.h @@ -75,7 +75,7 @@ public: const u_char* Authentication() const { return auth; } - RecordValPtr BuildVal(const std::unique_ptr& inner) const; + RecordValPtr BuildVal(const std::shared_ptr& inner) const; protected: bool DoParse(const u_char* data, int& len, bool found_orig, bool found_au); diff --git a/src/analyzer/protocol/vxlan/VXLAN.cc b/src/analyzer/protocol/vxlan/VXLAN.cc index e5681db8ee..974a033b0a 100644 --- a/src/analyzer/protocol/vxlan/VXLAN.cc +++ b/src/analyzer/protocol/vxlan/VXLAN.cc @@ -60,6 +60,7 @@ void VXLAN_Analyzer::DeliverPacket(int len, const u_char* data, bool orig, uint6 EncapsulatingConn inner(Conn(), BifEnum::Tunnel::VXLAN); outer->Add(inner); + int encap_index = outer->Depth(); int vni = (data[4] << 16) + (data[5] << 8) + (data[6] << 0); @@ -88,8 +89,14 @@ void VXLAN_Analyzer::DeliverPacket(int len, const u_char* data, bool orig, uint6 ProtocolConfirmation(); if ( vxlan_packet ) - Conn()->EnqueueEvent(vxlan_packet, nullptr, ConnVal(), pkt.ip_hdr->ToPktHdrVal(), - val_mgr->Count(vni)); + { + EncapsulatingConn* ec = pkt.encap->At(encap_index); + if ( ec && ec->ip_hdr ) + { + Conn()->EnqueueEvent(vxlan_packet, nullptr, ConnVal(), ec->ip_hdr->ToPktHdrVal(), + val_mgr->Count(vni)); + } + } } } // namespace zeek::analyzer::vxlan diff --git a/src/iosource/Packet.h b/src/iosource/Packet.h index 8c61abea3d..0705f8fb4e 100644 --- a/src/iosource/Packet.h +++ b/src/iosource/Packet.h @@ -218,7 +218,7 @@ public: * The IP header for this packet. This is filled in by the IP analyzer * during processing if the packet contains an IP header. */ - std::unique_ptr ip_hdr = nullptr; + std::shared_ptr ip_hdr = nullptr; /** * The protocol of the packet. This is used by the tunnel analyzers to diff --git a/src/packet_analysis/protocol/icmp/ICMP.cc b/src/packet_analysis/protocol/icmp/ICMP.cc index 18a89c16fb..468d7e11df 100644 --- a/src/packet_analysis/protocol/icmp/ICMP.cc +++ b/src/packet_analysis/protocol/icmp/ICMP.cc @@ -72,7 +72,7 @@ void ICMPAnalyzer::DeliverPacket(Connection* c, double t, bool is_orig, int rema adapter->PacketContents(data + 8, std::min(len, remaining) - 8); const struct icmp* icmpp = (const struct icmp*)data; - const std::unique_ptr& ip = pkt->ip_hdr; + const std::shared_ptr& ip = pkt->ip_hdr; if ( ! zeek::detail::ignore_checksums && ! GetIgnoreChecksumsNets()->Contains(ip->IPHeaderSrcAddr()) && remaining >= len ) diff --git a/src/packet_analysis/protocol/ip/IP.cc b/src/packet_analysis/protocol/ip/IP.cc index cb45f02e7e..ef244f622c 100644 --- a/src/packet_analysis/protocol/ip/IP.cc +++ b/src/packet_analysis/protocol/ip/IP.cc @@ -51,7 +51,7 @@ bool IPAnalyzer::AnalyzePacket(size_t len, const uint8_t* data, Packet* packet) // This is a unique pointer because of the mass of early returns from this method. if ( protocol == 4 ) { - packet->ip_hdr = std::make_unique(ip, false); + packet->ip_hdr = std::make_shared(ip, false); packet->l3_proto = L3_IPV4; } else if ( protocol == 6 ) @@ -62,7 +62,7 @@ bool IPAnalyzer::AnalyzePacket(size_t len, const uint8_t* data, Packet* packet) return false; } - packet->ip_hdr = std::make_unique((const struct ip6_hdr*)data, false, len); + packet->ip_hdr = std::make_shared((const struct ip6_hdr*)data, false, len); packet->l3_proto = L3_IPV6; } else @@ -71,6 +71,15 @@ bool IPAnalyzer::AnalyzePacket(size_t len, const uint8_t* data, Packet* packet) return false; } + // If there's an encapsulation stack in this packet, meaning this packet is part of a chain + // of tunnels, make sure to store the IP header in the last flow in the stack so it can be + // used by previous analyzers as we return up the chain. + if ( packet->encap ) + { + if ( auto* ec = packet->encap->Last() ) + ec->ip_hdr = packet->ip_hdr; + } + const struct ip* ip4 = packet->ip_hdr->IP4_Hdr(); // TotalLen() returns the full length of the IP portion of the packet, including @@ -164,7 +173,7 @@ bool IPAnalyzer::AnalyzePacket(size_t len, const uint8_t* data, Packet* packet) { f = detail::fragment_mgr->NextFragment(run_state::processing_start_time, packet->ip_hdr, packet->data + hdr_size); - std::unique_ptr ih = f->ReassembledPkt(); + std::shared_ptr ih = f->ReassembledPkt(); if ( ! ih ) // It didn't reassemble into anything yet. @@ -275,7 +284,7 @@ bool IPAnalyzer::AnalyzePacket(size_t len, const uint8_t* data, Packet* packet) } int zeek::packet_analysis::IP::ParsePacket(int caplen, const u_char* const pkt, int proto, - std::unique_ptr& inner) + std::shared_ptr& inner) { if ( proto == IPPROTO_IPV6 ) { @@ -283,7 +292,7 @@ int zeek::packet_analysis::IP::ParsePacket(int caplen, const u_char* const pkt, return -1; const struct ip6_hdr* ip6 = (const struct ip6_hdr*)pkt; - inner = std::make_unique(ip6, false, caplen); + inner = std::make_shared(ip6, false, caplen); if ( (ip6->ip6_ctlun.ip6_un2_vfc & 0xF0) != 0x60 ) return -2; } @@ -294,7 +303,7 @@ int zeek::packet_analysis::IP::ParsePacket(int caplen, const u_char* const pkt, return -1; const struct ip* ip4 = (const struct ip*)pkt; - inner = std::make_unique(ip4, false); + inner = std::make_shared(ip4, false); if ( ip4->ip_v != 4 ) return -2; } diff --git a/src/packet_analysis/protocol/ip/IP.h b/src/packet_analysis/protocol/ip/IP.h index e9a62d6bf2..9aa00d99f3 100644 --- a/src/packet_analysis/protocol/ip/IP.h +++ b/src/packet_analysis/protocol/ip/IP.h @@ -57,5 +57,5 @@ private: * long enough to be an IP header, and \a inner is always non-null * for other return values. */ -int ParsePacket(int caplen, const u_char* const pkt, int proto, std::unique_ptr& inner); +int ParsePacket(int caplen, const u_char* const pkt, int proto, std::shared_ptr& inner); } diff --git a/src/packet_analysis/protocol/ip/IPBasedAnalyzer.cc b/src/packet_analysis/protocol/ip/IPBasedAnalyzer.cc index 876ce5ef2c..2994d320ba 100644 --- a/src/packet_analysis/protocol/ip/IPBasedAnalyzer.cc +++ b/src/packet_analysis/protocol/ip/IPBasedAnalyzer.cc @@ -32,7 +32,7 @@ bool IPBasedAnalyzer::AnalyzePacket(size_t len, const uint8_t* data, Packet* pkt if ( ! BuildConnTuple(len, data, pkt, tuple) ) return false; - const std::unique_ptr& ip_hdr = pkt->ip_hdr; + const std::shared_ptr& ip_hdr = pkt->ip_hdr; detail::ConnKey key(tuple); Connection* conn = session_mgr->FindConnection(key); diff --git a/src/packet_analysis/protocol/iptunnel/IPTunnel.cc b/src/packet_analysis/protocol/iptunnel/IPTunnel.cc index dcde13a94a..23ae906943 100644 --- a/src/packet_analysis/protocol/iptunnel/IPTunnel.cc +++ b/src/packet_analysis/protocol/iptunnel/IPTunnel.cc @@ -44,7 +44,7 @@ bool IPTunnelAnalyzer::AnalyzePacket(size_t len, const uint8_t* data, Packet* pa BifEnum::Tunnel::Type tunnel_type = packet->tunnel_type; int gre_link_type = packet->gre_link_type; - std::unique_ptr inner = nullptr; + std::shared_ptr inner = nullptr; if ( gre_version != 0 ) { @@ -96,7 +96,7 @@ bool IPTunnelAnalyzer::AnalyzePacket(size_t len, const uint8_t* data, Packet* pa * Handles a packet that contains an IP header directly after the tunnel header. */ bool IPTunnelAnalyzer::ProcessEncapsulatedPacket(double t, const Packet* pkt, - const std::unique_ptr& inner, + const std::shared_ptr& inner, std::shared_ptr prev, const EncapsulatingConn& ec) { diff --git a/src/packet_analysis/protocol/iptunnel/IPTunnel.h b/src/packet_analysis/protocol/iptunnel/IPTunnel.h index 23383bffd4..71c7be7b7a 100644 --- a/src/packet_analysis/protocol/iptunnel/IPTunnel.h +++ b/src/packet_analysis/protocol/iptunnel/IPTunnel.h @@ -44,7 +44,7 @@ public: * @param ec The most-recently found depth of encapsulation. */ bool ProcessEncapsulatedPacket(double t, const Packet* pkt, - const std::unique_ptr& inner, + const std::shared_ptr& inner, std::shared_ptr prev, const EncapsulatingConn& ec); diff --git a/src/packet_analysis/protocol/tcp/TCP.cc b/src/packet_analysis/protocol/tcp/TCP.cc index cdd8a4c80f..6bfd02cff5 100644 --- a/src/packet_analysis/protocol/tcp/TCP.cc +++ b/src/packet_analysis/protocol/tcp/TCP.cc @@ -113,7 +113,7 @@ void TCPAnalyzer::DeliverPacket(Connection* c, double t, bool is_orig, int remai analyzer::tcp::TCP_Endpoint* endpoint = is_orig ? adapter->orig : adapter->resp; analyzer::tcp::TCP_Endpoint* peer = endpoint->peer; - const std::unique_ptr& ip = pkt->ip_hdr; + const std::shared_ptr& ip = pkt->ip_hdr; if ( ! ValidateChecksum(ip.get(), tp, endpoint, len, remaining, adapter) ) return; diff --git a/src/packet_analysis/protocol/tcp/TCPSessionAdapter.cc b/src/packet_analysis/protocol/tcp/TCPSessionAdapter.cc index c342f1f3b8..53027538e8 100644 --- a/src/packet_analysis/protocol/tcp/TCPSessionAdapter.cc +++ b/src/packet_analysis/protocol/tcp/TCPSessionAdapter.cc @@ -542,7 +542,7 @@ static int32_t update_last_seq(analyzer::tcp::TCP_Endpoint* endpoint, uint32_t l } void TCPSessionAdapter::Process(bool is_orig, const struct tcphdr* tp, int len, - const std::unique_ptr& ip, const u_char* data, + const std::shared_ptr& ip, const u_char* data, int remaining) { analyzer::tcp::TCP_Flags flags(tp); diff --git a/src/packet_analysis/protocol/tcp/TCPSessionAdapter.h b/src/packet_analysis/protocol/tcp/TCPSessionAdapter.h index 5025f0299b..d20b28765b 100644 --- a/src/packet_analysis/protocol/tcp/TCPSessionAdapter.h +++ b/src/packet_analysis/protocol/tcp/TCPSessionAdapter.h @@ -33,7 +33,7 @@ public: explicit TCPSessionAdapter(Connection* conn); ~TCPSessionAdapter() override; - void Process(bool is_orig, const struct tcphdr* tp, int len, const std::unique_ptr& ip, + void Process(bool is_orig, const struct tcphdr* tp, int len, const std::shared_ptr& ip, const u_char* data, int remaining); void EnableReassembly(); diff --git a/src/packet_analysis/protocol/udp/UDP.cc b/src/packet_analysis/protocol/udp/UDP.cc index 374f69c50c..6f60386d3e 100644 --- a/src/packet_analysis/protocol/udp/UDP.cc +++ b/src/packet_analysis/protocol/udp/UDP.cc @@ -86,7 +86,7 @@ void UDPAnalyzer::DeliverPacket(Connection* c, double t, bool is_orig, int remai int len = pkt->ip_hdr->PayloadLen(); const struct udphdr* up = (const struct udphdr*)data; - const std::unique_ptr& ip = pkt->ip_hdr; + const std::shared_ptr& ip = pkt->ip_hdr; adapter->DeliverPacket(len, data, is_orig, -1, ip.get(), remaining);