genode/repos/os/src/server/nic_bridge/nic.cc

141 lines
3.7 KiB
C++

/*
* \brief NIC handler
* \author Stefan Kalkowski
* \date 2013-05-24
*/
/*
* Copyright (C) 2013-2017 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU Affero General Public License version 3.
*/
#include <base/env.h>
#include <net/ethernet.h>
#include <net/arp.h>
#include <net/ipv4.h>
#include <net/udp.h>
#include <net/dhcp.h>
#include "component.h"
using namespace Net;
using namespace Genode;
bool Net::Nic::handle_arp(Ethernet_frame &eth,
Size_guard &size_guard)
{
/* ignore broken packets */
Arp_packet &arp = eth.data<Arp_packet>(size_guard);
if (!arp.ethernet_ipv4())
return true;
/* look whether the IP address is one of our client's */
Ipv4_address_node *node = vlan().ip_tree.first();
if (node)
node = node->find_by_address(arp.dst_ip());
if (node) {
if (arp.opcode() == Arp_packet::REQUEST) {
/*
* The ARP packet gets re-written, we interchange source
* and destination MAC and IP addresses, and set the opcode
* to reply, and then push the packet back to the NIC driver.
*/
Ipv4_address old_src_ip = arp.src_ip();
arp.opcode(Arp_packet::REPLY);
arp.dst_mac(arp.src_mac());
arp.src_mac(mac());
arp.src_ip(arp.dst_ip());
arp.dst_ip(old_src_ip);
eth.dst(arp.dst_mac());
/* set our MAC as sender */
eth.src(mac());
send(&eth, size_guard.total_size());
} else {
/* overwrite destination MAC */
arp.dst_mac(node->component().mac_address().addr);
eth.dst(node->component().mac_address().addr);
node->component().send(&eth, size_guard.total_size());
}
return false;
}
return true;
}
bool Net::Nic::handle_ip(Ethernet_frame &eth,
Size_guard &size_guard)
{
/* is it an UDP packet ? */
Ipv4_packet &ip = eth.data<Ipv4_packet>(size_guard);
if (ip.protocol() == Ipv4_packet::Protocol::UDP)
{
/* is it a DHCP packet ? */
Udp_packet &udp = ip.data<Udp_packet>(size_guard);
if (Dhcp_packet::is_dhcp(&udp)) {
/* check for DHCP ACKs containing new client ips */
Dhcp_packet &dhcp = udp.data<Dhcp_packet>(size_guard);
if (dhcp.op() == Dhcp_packet::REPLY) {
try {
Dhcp_packet::Message_type const msg_type =
dhcp.option<Dhcp_packet::Message_type_option>().value();
/*
* Extract the IP address and set it in the client's
* session-component
*/
if (msg_type == Dhcp_packet::Message_type::ACK) {
Mac_address_node *node =
vlan().mac_tree.first();
if (node)
node = node->find_by_address(dhcp.client_mac());
if (node)
node->component().set_ipv4_address(dhcp.yiaddr());
}
}
catch (Dhcp_packet::Option_not_found) { }
}
}
}
/* is it an unicast message to one of our clients ? */
if (eth.dst() == mac()) {
Ipv4_address_node *node = vlan().ip_tree.first();
if (node) {
node = node->find_by_address(ip.dst());
if (node) {
/* overwrite destination MAC */
eth.dst(node->component().mac_address().addr);
/* deliver the packet to the client */
node->component().send(&eth, size_guard.total_size());
return false;
}
}
}
return true;
}
Net::Nic::Nic(Genode::Env &env,
Genode::Heap &heap,
Net::Vlan &vlan,
bool const &verbose,
Session_label const &label)
: Packet_handler(env.ep(), vlan, label, verbose),
_tx_block_alloc(&heap),
_nic(env, &_tx_block_alloc, BUF_SIZE, BUF_SIZE),
_mac(_nic.mac_address().addr)
{
_nic.rx_channel()->sigh_ready_to_ack(_sink_ack);
_nic.rx_channel()->sigh_packet_avail(_sink_submit);
_nic.tx_channel()->sigh_ack_avail(_source_ack);
_nic.tx_channel()->sigh_ready_to_submit(_source_submit);
_nic.link_state_sigh(_client_link_state);
}