void IP4::transmit(Packet_ptr pckt) { assert(pckt->size() > sizeof(IP4::full_header)); auto ip4_pckt = std::static_pointer_cast<PacketIP4>(pckt); ip4_pckt->make_flight_ready(); IP4::ip_header& hdr = ip4_pckt->ip4_header(); // Create local and target subnets addr target = hdr.daddr & stack_.netmask(); addr local = stack_.ip_addr() & stack_.netmask(); // Compare subnets to know where to send packet pckt->next_hop(target == local ? hdr.daddr : stack_.router()); debug("<IP4 TOP> Next hop for %s, (netmask %s, local IP: %s, gateway: %s) == %s\n", hdr.daddr.str().c_str(), stack_.netmask().str().c_str(), stack_.ip_addr().str().c_str(), stack_.router().str().c_str(), target == local ? "DIRECT" : "GATEWAY"); debug("<IP4 transmit> my ip: %s, Next hop: %s, Packet size: %i IP4-size: %i\n", stack_.ip_addr().str().c_str(), pckt->next_hop().str().c_str(), pckt->size(), ip4_pckt->ip4_segment_size() ); linklayer_out_(pckt); }
void Ethernet::receive(Packet_ptr pckt) { Expects(pckt->size() > 0); header* eth = reinterpret_cast<header*>(pckt->buffer()); /** Do we pass on ethernet headers? As for now, yes. data += sizeof(header); len -= sizeof(header); */ debug2("<Ethernet IN> %s => %s , Eth.type: 0x%x ", eth->src.str().c_str(), eth->dest.str().c_str(), eth->type); // Stat increment packets received packets_rx_++; bool dropped = false; switch(eth->type) { case ETH_IP4: debug2("IPv4 packet\n"); ip4_upstream_(std::move(pckt)); break; case ETH_IP6: debug2("IPv6 packet\n"); ip6_upstream_(std::move(pckt)); break; case ETH_ARP: debug2("ARP packet\n"); arp_upstream_(std::move(pckt)); break; case ETH_WOL: dropped = true; debug2("Wake-on-LAN packet\n"); break; case ETH_VLAN: dropped = true; debug("VLAN tagged frame (not yet supported)"); break; default: dropped = true; // This might be 802.3 LLC traffic if (net::ntohs(eth->type) > 1500) { debug2("<Ethernet> UNKNOWN ethertype 0x%x\n", ntohs(eth->type)); }else { debug2("IEEE802.3 Length field: 0x%x\n", ntohs(eth->type)); } break; } if(dropped) packets_dropped_++; }
void Listener::segment_arrived(Packet_ptr packet) { debug2("<Listener::segment_arrived> Received packet: %s\n", packet->to_string().c_str()); // if it's an reply to any of our half-open connections if(!packet->isset(SYN)) { // if there is a connection waiting for the packet for(auto conn : syn_queue_) // take a copy to avoid conn->segment_arrived to make a reference invalid { if(conn->remote() == packet->source()) { debug("<Listener::segment_arrived> Found packet receiver: %s\n", conn->to_string().c_str()); conn->segment_arrived(packet); debug2("<Listener::segment_arrived> Connection done handling segment\n"); return; } } } // if it's a new attempt (SYN) else { // Stat increment number of connection attempts host_.connection_attempts_++; // if we don't like this client, do nothing if(! on_accept_(packet->source()) ) return; // remove oldest connection if queue is full debug2("<Listener::segment_arrived> SynQueue: %u\n", syn_queue_.size()); if(syn_queue_full()) { debug2("<Listener::segment_arrived> Queue is full\n"); Expects(not syn_queue_.empty()); debug("<Listener::segment_arrived> Connection %s dropped to make room for new connection\n", syn_queue_.back()->to_string().c_str()); syn_queue_.pop_back(); } auto& conn = *(syn_queue_.emplace(syn_queue_.begin(), std::make_shared<Connection>( host_, port_, packet->source() ))); // Call Listener::connected when Connection is connected conn->on_connect(ConnectCallback::from<Listener, &Listener::connected>(this)); conn->_on_cleanup(CleanupCallback::from<Listener, &Listener::remove>(this)); // Open connection conn->open(false); Ensures(conn->is_listening()); debug("<Listener::segment_arrived> Connection %s created\n", conn->to_string().c_str()); conn->segment_arrived(packet); debug2("<Listener::segment_arrived> Connection done handling segment\n"); return; } debug2("<Listener::segment_arrived> No receipent\n"); }
void Arp::receive(Packet_ptr pckt) { PRINT("<ARP handler> got %i bytes of data\n", pckt->size()); header* hdr = reinterpret_cast<header*>(pckt->layer_begin()); /// cache entry this->cache(hdr->sipaddr, hdr->shwaddr); /// always try to ship waiting packets when someone talks auto waiting = waiting_packets_.find(hdr->sipaddr); if (waiting != waiting_packets_.end()) { PRINT("<Arp> Had a packet waiting for this IP. Sending\n"); transmit(std::move(waiting->second.pckt), hdr->sipaddr); waiting_packets_.erase(waiting); } switch(hdr->opcode) { case H_request: { // Stat increment requests received requests_rx_++; PRINT("<Arp> %s is looking for %s\n", hdr->sipaddr.str().c_str(), hdr->dipaddr.str().c_str()); if (hdr->dipaddr == inet_.ip_addr()) { // The packet is for us. Respond. arp_respond(hdr, inet_.ip_addr()); } else if (proxy_ and proxy_(hdr->dipaddr)){ // The packet is for an IP to which we know a route arp_respond(hdr, hdr->dipaddr); } else { // Drop PRINT("\t NO MATCH for My IP (%s). DROP!\n", inet_.ip_addr().str().c_str()); } break; } case H_reply: { // Stat increment replies received replies_rx_++; PRINT("\t ARP REPLY: %s belongs to %s (waiting: %u)\n", hdr->sipaddr.str().c_str(), hdr->shwaddr.str().c_str(), waiting_packets_.size()); break; } default: PRINT("\t UNKNOWN OPCODE\n"); break; } //< switch(hdr->opcode) }
/* Add a packet to this packet chain. */ void chain(Packet_ptr p) noexcept { if (!chain_) { chain_ = p; last_ = p; } else { last_->chain(p); last_ = p->last_in_chain() ? p->last_in_chain() : p; assert(last_); } }
void Arp::await_resolution(Packet_ptr pckt, IP4::addr){ auto queue = waiting_packets_.find(pckt->next_hop()); if (queue != waiting_packets_.end()) { debug("<ARP Resolve> Packets allready queueing for this IP\n"); queue->second->chain(pckt); } else { debug("<ARP Resolve> This is the first packet going to that IP\n"); waiting_packets_.emplace(std::make_pair(pckt->next_hop(), pckt)); } }
int Arp::transmit(Packet_ptr pckt){ assert(pckt->size()); /** Get destination IP from IP header */ IP4::ip_header* iphdr = (IP4::ip_header*)(pckt->buffer() + sizeof(Ethernet::header)); IP4::addr sip = iphdr->saddr; IP4::addr dip = pckt->next_hop(); debug2("<ARP -> physical> Transmitting %i bytes to %s \n", pckt->size(),dip.str().c_str()); Ethernet::addr mac; if (iphdr->daddr == IP4::INADDR_BCAST) { // when broadcasting our source IP should be either // our own IP or 0.0.0.0 static const IP4::addr INADDR_NONE {{0}}; if (sip != ip_ && sip != INADDR_NONE) { debug2("<ARP> Dropping outbound broadcast packet due to " "invalid source IP %s\n", sip.str().c_str()); return -1; } mac = Ethernet::addr::BROADCAST_FRAME; } else { if (sip != ip_) { debug2("<ARP -> physical> Not bound to source IP %s. My IP is %s. DROP!\n", sip.str().c_str(), ip_.str().c_str()); return -1; } // If we don't have a cached IP, get mac from next-hop (HĂ„reks c001 hack) if (!is_valid_cached(dip)) return arp_resolver_(pckt); // Get mac from cache mac = cache_[dip].mac_; } /** Attach next-hop mac and ethertype to ethernet header */ Ethernet::header* ethhdr = (Ethernet::header*)pckt->buffer(); ethhdr->src = mac_; ethhdr->dest.major = mac.major; ethhdr->dest.minor = mac.minor; ethhdr->type = Ethernet::ETH_IP4; debug2("<ARP -> physical> Sending packet to %s \n",mac.str().c_str()); return linklayer_out_(pckt); }
size_t Connection::fill_packet(Packet_ptr packet, const char* buffer, size_t n, Seq seq) { Expects(!packet->has_tcp_data()); auto written = packet->fill(buffer, std::min(n, (size_t)SMSS())); packet->set_seq(seq).set_ack(cb.RCV.NXT).set_flag(ACK); Ensures(written <= n); return written; }
int Arp::bottom(Packet_ptr pckt) { debug2("<ARP handler> got %i bytes of data \n", pckt->size()); header* hdr = (header*) pckt->buffer(); //debug2("\t OPCODE: 0x%x \n",hdr->opcode); //debug2("Chaching IP %s for %s \n", hdr->sipaddr.str().c_str() , hdr->shwaddr.str().c_str()) debug2("Have valid cache? %s \n",is_valid_cached(hdr->sipaddr) ? "YES":"NO"); cache(hdr->sipaddr, hdr->shwaddr); switch(hdr->opcode){ case H_request: debug2("\t ARP REQUEST: "); debug2("%s is looking for %s \n", hdr->sipaddr.str().c_str(),hdr->dipaddr.str().c_str()); if (hdr->dipaddr == ip_) arp_respond(hdr); else { debug2("\t NO MATCH for My IP (%s). DROP!\n", ip().str().c_str()); } break; case H_reply: { debug2("\t ARP REPLY: %s belongs to %s\n", hdr->sipaddr.str().c_str(), hdr->shwaddr.str().c_str()); auto waiting = waiting_packets_.find(hdr->sipaddr); if (waiting != waiting_packets_.end()) { debug ("Had a packet waiting for this IP. Sending\n"); transmit(waiting->second); waiting_packets_.erase(waiting); } } break; default: debug2("\t UNKNOWN OPCODE \n"); break; } // Free the buffer (We're leaf node for this one's path) // @todo Freeing here corrupts the outgoing frame. Why? //free(data); return 0 + 0 * pckt->size(); // yep, it's what you think it is (and what's that?!) };
int Ethernet::bottom(Packet_ptr pckt) { assert(pckt->size() > 0); header* eth = (header*) pckt->buffer(); /** Do we pass on ethernet headers? As for now, yes. data += sizeof(header); len -= sizeof(header); */ debug2("<Ethernet IN> %s => %s , Eth.type: 0x%x ", eth->src.str().c_str(), eth->dest.str().c_str(),eth->type); switch(eth->type){ case ETH_IP4: debug2("IPv4 packet \n"); return ip4_handler_(pckt); case ETH_IP6: debug2("IPv6 packet \n"); return ip6_handler_(pckt); case ETH_ARP: debug2("ARP packet \n"); return arp_handler_(pckt); case ETH_WOL: debug2("Wake-on-LAN packet \n"); break; case ETH_VLAN: debug("VLAN tagged frame (not yet supported)"); default: // This might be 802.3 LLC traffic if (net::ntohs(eth->type) > 1500){ debug("<Ethernet> UNKNOWN ethertype 0x%x\n",ntohs(eth->type)); }else{ debug2("IEEE802.3 Length field: 0x%x\n",ntohs(eth->type)); } break; } return -1; }
void Arp::bottom(Packet_ptr pckt) { debug2("<ARP handler> got %i bytes of data\n", pckt->size()); header* hdr = reinterpret_cast<header*>(pckt->buffer()); debug2("Have valid cache? %s\n", is_valid_cached(hdr->sipaddr) ? "YES" : "NO"); cache(hdr->sipaddr, hdr->shwaddr); switch(hdr->opcode) { case H_request: { // Stat increment requests received requests_rx_++; debug2("\t ARP REQUEST: "); debug2("%s is looking for %s\n", hdr->sipaddr.str().c_str(), hdr->dipaddr.str().c_str()); if (hdr->dipaddr == inet_.ip_addr()) { arp_respond(hdr); } else { debug2("\t NO MATCH for My IP (%s). DROP!\n", inet_.ip_addr().str().c_str()); } break; } case H_reply: { // Stat increment replies received replies_rx_++; debug2("\t ARP REPLY: %s belongs to %s (waiting: %u)\n", hdr->sipaddr.str().c_str(), hdr->shwaddr.str().c_str(), waiting_packets_.size()); auto waiting = waiting_packets_.find(hdr->sipaddr); if (waiting != waiting_packets_.end()) { debug("Had a packet waiting for this IP. Sending\n"); transmit(std::move(waiting->second)); waiting_packets_.erase(waiting); } break; } default: debug2("\t UNKNOWN OPCODE\n"); break; } //< switch(hdr->opcode) }
int Arp::arp_resolve(Packet_ptr pckt){ debug("<ARP RESOLVE> %s \n", pckt->next_hop().str().c_str()); await_resolution(pckt, pckt->next_hop()); auto req = std::static_pointer_cast<PacketArp>(inet_.createPacket(sizeof(header))); req->init(mac_, ip_); req->set_dest_mac(Ethernet::addr::BROADCAST_FRAME); req->set_dest_ip(pckt->next_hop()); req->set_opcode(H_request); linklayer_out_(req); return 0; }
int Ethernet::transmit(Packet_ptr pckt){ header* hdr = (header*)pckt->buffer(); // Verify ethernet header assert(hdr->dest.major != 0 || hdr->dest.minor !=0); assert(hdr->type != 0); // Add source address hdr->src.major = _mac.major; hdr->src.minor = _mac.minor; debug2("<Ethernet OUT> Transmitting %i b, from %s -> %s. Type: %i \n", pckt->size(),hdr->src.str().c_str(), hdr->dest.str().c_str(),hdr->type); return physical_out_(pckt); }
void IP4::bottom(Packet_ptr pckt) { debug2("<IP4 handler> got the data.\n"); auto data = pckt->buffer(); ip_header* hdr = &reinterpret_cast<full_header*>(data)->ip_hdr; debug2("\t Source IP: %s Dest.IP: %s\n", hdr->saddr.str().c_str(), hdr->daddr.str().c_str()); switch(hdr->protocol){ case IP4_ICMP: debug2("\t Type: ICMP\n"); icmp_handler_(pckt); break; case IP4_UDP: debug2("\t Type: UDP\n"); udp_handler_(pckt); break; case IP4_TCP: tcp_handler_(pckt); debug2("\t Type: TCP\n"); break; default: debug("\t Type: UNKNOWN %i\n", hdr->protocol); break; } }
void Arp::arp_resolve(Packet_ptr pckt) { debug("<ARP RESOLVE> %s\n", pckt->next_hop().str().c_str()); const auto next_hop = pckt->next_hop(); await_resolution(std::move(pckt), next_hop); auto req = static_unique_ptr_cast<PacketArp>(inet_.create_packet(sizeof(header))); req->init(mac_, inet_.ip_addr()); req->set_dest_mac(Ethernet::BROADCAST_FRAME); req->set_dest_ip(next_hop); req->set_opcode(H_request); // Stat increment requests sent requests_tx_++; linklayer_out_(std::move(req)); }
void ICMPv4::bottom(Packet_ptr pckt) { if (pckt->size() < sizeof(full_header)) // Drop if not a full header return; full_header* full_hdr = reinterpret_cast<full_header*>(pckt->buffer()); icmp_header* hdr = &full_hdr->icmp_hdr; #ifdef DEBUG auto ip_address = full_hdr->ip_hdr.saddr.str().c_str(); #endif switch(hdr->type) { case (ICMP_ECHO): debug("<ICMP> PING from %s\n", ip_address); ping_reply(full_hdr, pckt->size()); break; case (ICMP_ECHO_REPLY): debug("<ICMP> PING Reply from %s\n", ip_address); break; } }
int ICMP::bottom(Packet_ptr pckt){ if (pckt->size() < sizeof(full_header)) //Drop if not a full header. return -1; full_header* full_hdr = (full_header*)pckt->buffer(); icmp_header* hdr = &full_hdr->icmp_hdr; switch(hdr->type) { case (ICMP_ECHO): debug("<ICMP> PING from %s \n",full_hdr->ip_hdr.saddr.str().c_str()); ping_reply(full_hdr); break; case (ICMP_ECHO_REPLY): debug("<ICMP> PING Reply from %s \n",full_hdr->ip_hdr.saddr.str().c_str()); break; } return 0; }
void Arp::transmit(Packet_ptr pckt, IP4::addr next_hop) { Expects(pckt->size()); PRINT("<ARP -> physical> Transmitting %u bytes to %s\n", (uint32_t) pckt->size(), next_hop.str().c_str()); MAC::Addr dest_mac; if (next_hop == IP4::ADDR_BCAST) { dest_mac = MAC::BROADCAST; } else { #ifdef ARP_PASSTHROUGH extern MAC::Addr linux_tap_device; dest_mac = linux_tap_device; #else // If we don't have a cached IP, perform address resolution auto cache_entry = cache_.find(next_hop); if (UNLIKELY(cache_entry == cache_.end())) { PRINT("<ARP> No cache entry for IP %s. Resolving. \n", next_hop.to_string().c_str()); await_resolution(std::move(pckt), next_hop); return; } // Get MAC from cache dest_mac = cache_[next_hop].mac(); #endif PRINT("<ARP> Found cache entry for IP %s -> %s \n", next_hop.to_string().c_str(), dest_mac.to_string().c_str()); } // Move chain to linklayer linklayer_out_(std::move(pckt), dest_mac, Ethertype::IP4); }
void Arp::transmit(Packet_ptr pckt) { assert(pckt->size()); /** Get destination IP from IP header */ IP4::ip_header* iphdr = reinterpret_cast<IP4::ip_header*>(pckt->buffer() + sizeof(Ethernet::header)); IP4::addr sip = iphdr->saddr; IP4::addr dip = pckt->next_hop(); debug2("<ARP -> physical> Transmitting %i bytes to %s\n", pckt->size(), dip.str().c_str()); Ethernet::addr dest_mac; if (iphdr->daddr == IP4::INADDR_BCAST) { // When broadcasting our source IP should be either // our own IP or 0.0.0.0 if (sip != inet_.ip_addr() && sip != IP4::INADDR_ANY) { debug2("<ARP> Dropping outbound broadcast packet due to " "invalid source IP %s\n", sip.str().c_str()); return; } // mui importante dest_mac = Ethernet::BROADCAST_FRAME; } else { if (sip != inet_.ip_addr()) { debug2("<ARP -> physical> Not bound to source IP %s. My IP is %s. DROP!\n", sip.str().c_str(), inet_.ip_addr().str().c_str()); return; } // If we don't have a cached IP, perform address resolution if (!is_valid_cached(dip)) { arp_resolver_(std::move(pckt)); return; } // Get MAC from cache dest_mac = cache_[dip].mac_; } /** Attach next-hop mac and ethertype to ethernet header */ auto* ethhdr = reinterpret_cast<Ethernet::header*>(pckt->buffer()); ethhdr->src = mac_; ethhdr->dest = dest_mac; ethhdr->type = Ethernet::ETH_IP4; /** Update chain as well */ auto* next = pckt->tail(); while(next) { auto* headur = reinterpret_cast<Ethernet::header*>(next->buffer()); headur->src = mac_; headur->dest = dest_mac; headur->type = Ethernet::ETH_IP4; next = next->tail(); } debug2("<ARP -> physical> Sending packet to %s\n", mac_.str().c_str()); linklayer_out_(std::move(pckt)); }
void Listener::segment_arrived(Packet_ptr packet) { debug2("<Listener::segment_arrived> Received packet: %s\n", packet->to_string().c_str()); auto it = std::find_if(syn_queue_.begin(), syn_queue_.end(), [dest = packet->source()] (Connection_ptr conn) { return conn->remote() == dest; }); // if it's an reply to any of our half-open connections if(it != syn_queue_.end()) { auto conn = *it; debug("<Listener::segment_arrived> Found packet receiver: %s\n", conn->to_string().c_str()); conn->segment_arrived(std::move(packet)); debug2("<Listener::segment_arrived> Connection done handling segment\n"); return; } // if it's a new attempt (SYN) else { // don't waste time if the packet does not have SYN if(UNLIKELY(not packet->isset(SYN) or packet->has_tcp_data())) { host_.send_reset(*packet); return; } // Stat increment number of connection attempts host_.connection_attempts_++; // if we don't like this client, do nothing if(UNLIKELY(on_accept_(packet->source()) == false)) return; // remove oldest connection if queue is full debug2("<Listener::segment_arrived> SynQueue: %u\n", syn_queue_.size()); // SYN queue is full if(syn_queue_.size() >= host_.max_syn_backlog()) { debug2("<Listener::segment_arrived> Queue is full\n"); Expects(not syn_queue_.empty()); debug("<Listener::segment_arrived> Connection %s dropped to make room for new connection\n", syn_queue_.back()->to_string().c_str()); syn_queue_.pop_back(); } auto& conn = *(syn_queue_.emplace( syn_queue_.cbegin(), std::make_shared<Connection>(host_, packet->destination(), packet->source(), ConnectCallback{this, &Listener::connected}) ) ); conn->_on_cleanup({this, &Listener::remove}); // Open connection conn->open(false); Ensures(conn->is_listening()); debug("<Listener::segment_arrived> Connection %s created\n", conn->to_string().c_str()); conn->segment_arrived(std::move(packet)); debug2("<Listener::segment_arrived> Connection done handling segment\n"); return; } debug2("<Listener::segment_arrived> No receipent\n"); }
void Inet::error_report(Error& err, Packet_ptr orig_pckt) { // if its a forged packet, it might be too small if (orig_pckt->size() < 40) return; auto pckt_ip4 = static_unique_ptr_cast<PacketIP4>(std::move(orig_pckt)); // Get the destination to the original packet const Socket dest = [] (std::unique_ptr<PacketIP4>& pkt)->Socket { // if its a forged packet, it might not be IPv4 if (pkt->is_ipv4() == false) return {}; // switch on IP4 protocol switch (pkt->ip_protocol()) { case Protocol::UDP: { const auto& udp = static_cast<const PacketUDP&>(*pkt); return udp.destination(); } case Protocol::TCP: { auto tcp = tcp::Packet4_view(std::move(pkt)); auto dst = tcp.destination(); pkt = static_unique_ptr_cast<PacketIP4>(tcp.release()); return dst; } default: return {}; } }(pckt_ip4); bool too_big = false; if (err.is_icmp()) { auto* icmp_err = dynamic_cast<ICMP_error*>(&err); if (icmp_err == nullptr) { return; // not an ICMP error } if (icmp_err->is_too_big()) { // If Path MTU Discovery is not enabled, ignore the ICMP Datagram Too Big message if (not ip4_.path_mtu_discovery()) return; too_big = true; // We have received a response to a packet with an MTU that is too big for a node in the path, // and the packet has been dropped (the original packet was too big and the Don't Fragment bit was set) // Notify every protocol of the received MTU if any of the protocol's connections use the given // path (based on destination address) // Also need to notify the instance that sent the packet that the packet has been dropped, so // it can retransmit it // A Destination Unreachable: Fragmentation Needed ICMP error message has been received // And we'll notify the IP layer of the received MTU value // IP will create the path if it doesn't exist and only update the value if // the value is smaller than the already registered pmtu for this path/destination // If the received MTU value is zero, the method will use the original packet's Total Length // and Header Length values to estimate a new Path MTU value ip4_.update_path(dest, icmp_err->pmtu(), too_big, pckt_ip4->ip_total_length(), pckt_ip4->ip_header_length()); // The actual MTU for the path is set in the error object icmp_err->set_pmtu(ip4_.pmtu(dest)); } } if (too_big) { // Notify both transport layers in case they use the path udp_.error_report(err, dest); tcp_.error_report(err, dest); } else if (pckt_ip4->ip_protocol() == Protocol::UDP) { udp_.error_report(err, dest); } else if (pckt_ip4->ip_protocol() == Protocol::TCP) { tcp_.error_report(err, dest); } }