nic_router: no memcpy on self-written packets

Previously, all packets that the router wanted to sent were first prepared to
their final state and then copied at once into the packet stream RAM. This is
fine for packets that the router only passes through with modifying merely
a few values. But for packets that the router writes from scratch on its own,
it is better to compose the packet directly in the packet stream RAM.

Fix #2626
This commit is contained in:
Martin Stein
2017-12-20 15:56:47 +01:00
committed by Christian Helmuth
parent b6991f9c03
commit 57bfd09328
2 changed files with 171 additions and 168 deletions

View File

@@ -168,97 +168,91 @@ void Dhcp_client::_send(Message_type msg_type,
Ipv4_address client_ip, Ipv4_address client_ip,
Ipv4_address server_ip) Ipv4_address server_ip)
{ {
/* allocate buffer for the request */ enum { PKT_SIZE = 1024 };
enum { BUF_SIZE = 1024 }; using Size_guard = Size_guard_tpl<PKT_SIZE, Interface::Dhcp_msg_buffer_too_small>;
using Size_guard = Size_guard_tpl<BUF_SIZE, Interface::Dhcp_msg_buffer_too_small>;
void *buf;
try { _alloc.alloc(BUF_SIZE, &buf); }
catch (...) { throw Interface::Alloc_dhcp_msg_buffer_failed(); }
Mac_address client_mac = _interface.router_mac(); Mac_address client_mac = _interface.router_mac();
_interface.send(PKT_SIZE, [&] (void *pkt_base) {
/* create ETH header of the request */ /* create ETH header of the request */
Size_guard size; Size_guard size;
size.add(sizeof(Ethernet_frame)); size.add(sizeof(Ethernet_frame));
Ethernet_frame &eth = *reinterpret_cast<Ethernet_frame *>(buf); Ethernet_frame &eth = *reinterpret_cast<Ethernet_frame *>(pkt_base);
eth.dst(Mac_address(0xff)); eth.dst(Mac_address(0xff));
eth.src(client_mac); eth.src(client_mac);
eth.type(Ethernet_frame::Type::IPV4); eth.type(Ethernet_frame::Type::IPV4);
/* create IP header of the request */ /* create IP header of the request */
enum { IPV4_TIME_TO_LIVE = 64 }; enum { IPV4_TIME_TO_LIVE = 64 };
size_t const ip_off = size.curr(); size_t const ip_off = size.curr();
size.add(sizeof(Ipv4_packet)); size.add(sizeof(Ipv4_packet));
Ipv4_packet &ip = *eth.data<Ipv4_packet>(); Ipv4_packet &ip = *eth.data<Ipv4_packet>();
ip.header_length(sizeof(Ipv4_packet) / 4); ip.header_length(sizeof(Ipv4_packet) / 4);
ip.version(4); ip.version(4);
ip.diff_service(0); ip.diff_service(0);
ip.identification(0); ip.identification(0);
ip.flags(0); ip.flags(0);
ip.fragment_offset(0); ip.fragment_offset(0);
ip.time_to_live(IPV4_TIME_TO_LIVE); ip.time_to_live(IPV4_TIME_TO_LIVE);
ip.protocol(Ipv4_packet::Protocol::UDP); ip.protocol(Ipv4_packet::Protocol::UDP);
ip.src(client_ip); ip.src(client_ip);
ip.dst(Ipv4_address(0xff)); ip.dst(Ipv4_address(0xff));
/* create UDP header of the request */ /* create UDP header of the request */
size_t const udp_off = size.curr(); size_t const udp_off = size.curr();
size.add(sizeof(Udp_packet)); size.add(sizeof(Udp_packet));
Udp_packet &udp = *ip.data<Udp_packet>(); Udp_packet &udp = *ip.data<Udp_packet>();
udp.src_port(Port(Dhcp_packet::BOOTPC)); udp.src_port(Port(Dhcp_packet::BOOTPC));
udp.dst_port(Port(Dhcp_packet::BOOTPS)); udp.dst_port(Port(Dhcp_packet::BOOTPS));
/* create mandatory DHCP fields of the request */ /* create mandatory DHCP fields of the request */
size_t const dhcp_off = size.curr(); size_t const dhcp_off = size.curr();
size.add(sizeof(Dhcp_packet)); size.add(sizeof(Dhcp_packet));
Dhcp_packet &dhcp = *udp.data<Dhcp_packet>(); Dhcp_packet &dhcp = *udp.data<Dhcp_packet>();
dhcp.op(Dhcp_packet::REQUEST); dhcp.op(Dhcp_packet::REQUEST);
dhcp.htype(Dhcp_packet::Htype::ETH); dhcp.htype(Dhcp_packet::Htype::ETH);
dhcp.hlen(sizeof(Mac_address)); dhcp.hlen(sizeof(Mac_address));
dhcp.hops(0); dhcp.hops(0);
dhcp.xid(0x12345678); dhcp.xid(0x12345678);
dhcp.secs(0); dhcp.secs(0);
dhcp.flags(0); dhcp.flags(0);
dhcp.ciaddr(client_ip); dhcp.ciaddr(client_ip);
dhcp.yiaddr(Ipv4_address()); dhcp.yiaddr(Ipv4_address());
dhcp.siaddr(Ipv4_address()); dhcp.siaddr(Ipv4_address());
dhcp.giaddr(Ipv4_address()); dhcp.giaddr(Ipv4_address());
dhcp.client_mac(client_mac); dhcp.client_mac(client_mac);
dhcp.zero_fill_sname(); dhcp.zero_fill_sname();
dhcp.zero_fill_file(); dhcp.zero_fill_file();
dhcp.default_magic_cookie(); dhcp.default_magic_cookie();
/* append DHCP option fields to the request */ /* append DHCP option fields to the request */
Dhcp_packet::Options_aggregator<Size_guard> Dhcp_packet::Options_aggregator<Size_guard>
dhcp_opts(dhcp, size); dhcp_opts(dhcp, size);
dhcp_opts.append_option<Dhcp_packet::Message_type_option>(msg_type); dhcp_opts.append_option<Dhcp_packet::Message_type_option>(msg_type);
switch (msg_type) { switch (msg_type) {
case Message_type::DISCOVER: case Message_type::DISCOVER:
dhcp_opts.append_option<Dhcp_packet::Client_id>(client_mac); dhcp_opts.append_option<Dhcp_packet::Client_id>(client_mac);
dhcp_opts.append_option<Dhcp_packet::Max_msg_size>(BUF_SIZE - dhcp_off); dhcp_opts.append_option<Dhcp_packet::Max_msg_size>(PKT_SIZE - dhcp_off);
break; break;
case Message_type::REQUEST: case Message_type::REQUEST:
dhcp_opts.append_option<Dhcp_packet::Client_id>(client_mac); dhcp_opts.append_option<Dhcp_packet::Client_id>(client_mac);
dhcp_opts.append_option<Dhcp_packet::Max_msg_size>(BUF_SIZE - dhcp_off); dhcp_opts.append_option<Dhcp_packet::Max_msg_size>(PKT_SIZE - dhcp_off);
if (_state == State::REQUEST) { if (_state == State::REQUEST) {
dhcp_opts.append_option<Dhcp_packet::Requested_addr>(client_ip); dhcp_opts.append_option<Dhcp_packet::Requested_addr>(client_ip);
dhcp_opts.append_option<Dhcp_packet::Server_ipv4>(server_ip); dhcp_opts.append_option<Dhcp_packet::Server_ipv4>(server_ip);
}
break;
default:
throw Interface::Bad_send_dhcp_args();
} }
break; dhcp_opts.append_option<Dhcp_packet::Options_end>();
default: /* fill in header values that need the packet to be complete already */
throw Interface::Bad_send_dhcp_args(); udp.length(size.curr() - udp_off);
} udp.update_checksum(ip.src(), ip.dst());
dhcp_opts.append_option<Dhcp_packet::Options_end>(); ip.total_length(size.curr() - ip_off);
ip.checksum(Ipv4_packet::calculate_checksum(ip));
/* fill in header values that need the packet to be complete already */ });
udp.length(size.curr() - udp_off);
udp.update_checksum(ip.src(), ip.dst());
ip.total_length(size.curr() - ip_off);
ip.checksum(Ipv4_packet::calculate_checksum(ip));
/* send request to sender of request and free request buffer */
_interface.send(eth, size.curr());
_alloc.free(buf, BUF_SIZE);
} }

View File

@@ -291,84 +291,78 @@ void Interface::_send_dhcp_reply(Dhcp_server const &dhcp_srv,
Dhcp_packet::Message_type msg_type, Dhcp_packet::Message_type msg_type,
uint32_t xid) uint32_t xid)
{ {
/* allocate buffer for the reply */ enum { PKT_SIZE = 512 };
enum { BUF_SIZE = 512 }; using Size_guard = Size_guard_tpl<PKT_SIZE, Dhcp_msg_buffer_too_small>;
using Size_guard = Size_guard_tpl<BUF_SIZE, Dhcp_msg_buffer_too_small>; send(PKT_SIZE, [&] (void *pkt_base) {
void *buf;
try { _alloc.alloc(BUF_SIZE, &buf); }
catch (...) { throw Alloc_dhcp_msg_buffer_failed(); }
/* create ETH header of the reply */ /* create ETH header of the reply */
Size_guard size; Size_guard size;
size.add(sizeof(Ethernet_frame)); size.add(sizeof(Ethernet_frame));
Ethernet_frame &eth = *reinterpret_cast<Ethernet_frame *>(buf); Ethernet_frame &eth = *reinterpret_cast<Ethernet_frame *>(pkt_base);
eth.dst(client_mac); eth.dst(client_mac);
eth.src(_router_mac); eth.src(_router_mac);
eth.type(Ethernet_frame::Type::IPV4); eth.type(Ethernet_frame::Type::IPV4);
/* create IP header of the reply */ /* create IP header of the reply */
enum { IPV4_TIME_TO_LIVE = 64 }; enum { IPV4_TIME_TO_LIVE = 64 };
size_t const ip_off = size.curr(); size_t const ip_off = size.curr();
size.add(sizeof(Ipv4_packet)); size.add(sizeof(Ipv4_packet));
Ipv4_packet &ip = *eth.data<Ipv4_packet>(); Ipv4_packet &ip = *eth.data<Ipv4_packet>();
ip.header_length(sizeof(Ipv4_packet) / 4); ip.header_length(sizeof(Ipv4_packet) / 4);
ip.version(4); ip.version(4);
ip.diff_service(0); ip.diff_service(0);
ip.identification(0); ip.identification(0);
ip.flags(0); ip.flags(0);
ip.fragment_offset(0); ip.fragment_offset(0);
ip.time_to_live(IPV4_TIME_TO_LIVE); ip.time_to_live(IPV4_TIME_TO_LIVE);
ip.protocol(Ipv4_packet::Protocol::UDP); ip.protocol(Ipv4_packet::Protocol::UDP);
ip.src(_router_ip()); ip.src(_router_ip());
ip.dst(client_ip); ip.dst(client_ip);
/* create UDP header of the reply */ /* create UDP header of the reply */
size_t const udp_off = size.curr(); size_t const udp_off = size.curr();
size.add(sizeof(Udp_packet)); size.add(sizeof(Udp_packet));
Udp_packet &udp = *ip.data<Udp_packet>(); Udp_packet &udp = *ip.data<Udp_packet>();
udp.src_port(Port(Dhcp_packet::BOOTPS)); udp.src_port(Port(Dhcp_packet::BOOTPS));
udp.dst_port(Port(Dhcp_packet::BOOTPC)); udp.dst_port(Port(Dhcp_packet::BOOTPC));
/* create mandatory DHCP fields of the reply */ /* create mandatory DHCP fields of the reply */
size.add(sizeof(Dhcp_packet)); size.add(sizeof(Dhcp_packet));
Dhcp_packet &dhcp = *udp.data<Dhcp_packet>(); Dhcp_packet &dhcp = *udp.data<Dhcp_packet>();
dhcp.op(Dhcp_packet::REPLY); dhcp.op(Dhcp_packet::REPLY);
dhcp.htype(Dhcp_packet::Htype::ETH); dhcp.htype(Dhcp_packet::Htype::ETH);
dhcp.hlen(sizeof(Mac_address)); dhcp.hlen(sizeof(Mac_address));
dhcp.hops(0); dhcp.hops(0);
dhcp.xid(xid); dhcp.xid(xid);
dhcp.secs(0); dhcp.secs(0);
dhcp.flags(0); dhcp.flags(0);
dhcp.ciaddr(msg_type == Dhcp_packet::Message_type::INFORM ? client_ip : Ipv4_address()); dhcp.ciaddr(msg_type == Dhcp_packet::Message_type::INFORM ? client_ip : Ipv4_address());
dhcp.yiaddr(msg_type == Dhcp_packet::Message_type::INFORM ? Ipv4_address() : client_ip); dhcp.yiaddr(msg_type == Dhcp_packet::Message_type::INFORM ? Ipv4_address() : client_ip);
dhcp.siaddr(_router_ip()); dhcp.siaddr(_router_ip());
dhcp.giaddr(Ipv4_address()); dhcp.giaddr(Ipv4_address());
dhcp.client_mac(client_mac); dhcp.client_mac(client_mac);
dhcp.zero_fill_sname(); dhcp.zero_fill_sname();
dhcp.zero_fill_file(); dhcp.zero_fill_file();
dhcp.default_magic_cookie(); dhcp.default_magic_cookie();
/* append DHCP option fields to the reply */ /* append DHCP option fields to the reply */
Dhcp_packet::Options_aggregator<Size_guard> dhcp_opts(dhcp, size); Dhcp_packet::Options_aggregator<Size_guard> dhcp_opts(dhcp, size);
dhcp_opts.append_option<Dhcp_packet::Message_type_option>(msg_type); dhcp_opts.append_option<Dhcp_packet::Message_type_option>(msg_type);
dhcp_opts.append_option<Dhcp_packet::Server_ipv4>(_router_ip()); dhcp_opts.append_option<Dhcp_packet::Server_ipv4>(_router_ip());
dhcp_opts.append_option<Dhcp_packet::Ip_lease_time>(dhcp_srv.ip_lease_time().value / 1000 / 1000); dhcp_opts.append_option<Dhcp_packet::Ip_lease_time>(dhcp_srv.ip_lease_time().value / 1000 / 1000);
dhcp_opts.append_option<Dhcp_packet::Subnet_mask>(_ip_config().interface.subnet_mask()); dhcp_opts.append_option<Dhcp_packet::Subnet_mask>(_ip_config().interface.subnet_mask());
dhcp_opts.append_option<Dhcp_packet::Router_ipv4>(_router_ip()); dhcp_opts.append_option<Dhcp_packet::Router_ipv4>(_router_ip());
if (dhcp_srv.dns_server().valid()) { if (dhcp_srv.dns_server().valid()) {
dhcp_opts.append_option<Dhcp_packet::Dns_server_ipv4>(dhcp_srv.dns_server()); } dhcp_opts.append_option<Dhcp_packet::Dns_server_ipv4>(dhcp_srv.dns_server()); }
dhcp_opts.append_option<Dhcp_packet::Broadcast_addr>(_ip_config().interface.broadcast_address()); dhcp_opts.append_option<Dhcp_packet::Broadcast_addr>(_ip_config().interface.broadcast_address());
dhcp_opts.append_option<Dhcp_packet::Options_end>(); dhcp_opts.append_option<Dhcp_packet::Options_end>();
/* fill in header values that need the packet to be complete already */ /* fill in header values that need the packet to be complete already */
udp.length(size.curr() - udp_off); udp.length(size.curr() - udp_off);
udp.update_checksum(ip.src(), ip.dst()); udp.update_checksum(ip.src(), ip.dst());
ip.total_length(size.curr() - ip_off); ip.total_length(size.curr() - ip_off);
ip.checksum(Ipv4_packet::calculate_checksum(ip)); ip.checksum(Ipv4_packet::calculate_checksum(ip));
});
/* send reply to sender of request and free reply buffer */
send(eth, size.curr());
_alloc.free(buf, BUF_SIZE);
} }
@@ -675,19 +669,34 @@ void Interface::_handle_ip(Ethernet_frame &eth,
void Interface::_broadcast_arp_request(Ipv4_address const &ip) void Interface::_broadcast_arp_request(Ipv4_address const &ip)
{ {
Ethernet_frame_sized<sizeof(Arp_packet)> enum {
eth(Mac_address(0xff), _router_mac, Ethernet_frame::Type::ARP); ETH_HDR_SZ = sizeof(Ethernet_frame),
Arp_packet &arp = *eth.data<Arp_packet>(); ETH_DAT_SZ = sizeof(Arp_packet) + ETH_HDR_SZ >= Ethernet_frame::MIN_SIZE ?
arp.hardware_address_type(Arp_packet::ETHERNET); sizeof(Arp_packet) :
arp.protocol_address_type(Arp_packet::IPV4); Ethernet_frame::MIN_SIZE - ETH_HDR_SZ,
arp.hardware_address_size(sizeof(Mac_address)); ETH_CRC_SZ = sizeof(Genode::uint32_t),
arp.protocol_address_size(sizeof(Ipv4_address)); PKT_SIZE = ETH_HDR_SZ + ETH_DAT_SZ + ETH_CRC_SZ,
arp.opcode(Arp_packet::REQUEST); };
arp.src_mac(_router_mac); send(PKT_SIZE, [&] (void *pkt_base) {
arp.src_ip(_router_ip());
arp.dst_mac(Mac_address(0xff)); /* write Ethernet header */
arp.dst_ip(ip); Ethernet_frame &eth = *reinterpret_cast<Ethernet_frame *>(pkt_base);
send(eth, sizeof(eth)); eth.dst(Mac_address(0xff));
eth.src(_router_mac);
eth.type(Ethernet_frame::Type::ARP);
/* write ARP header */
Arp_packet &arp = *eth.data<Arp_packet>();
arp.hardware_address_type(Arp_packet::ETHERNET);
arp.protocol_address_type(Arp_packet::IPV4);
arp.hardware_address_size(sizeof(Mac_address));
arp.protocol_address_size(sizeof(Ipv4_address));
arp.opcode(Arp_packet::REQUEST);
arp.src_mac(_router_mac);
arp.src_ip(_router_ip());
arp.dst_mac(Mac_address(0xff));
arp.dst_ip(ip);
});
} }