mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2024-11-22 23:50:19 +00:00
3e8cf79efa
This adds a DHCPClient...Server that leases IPv4s.
300 lines
8.7 KiB
C++
300 lines
8.7 KiB
C++
/*
|
|
* Copyright (c) 2020, The SerenityOS developers.
|
|
* All rights reserved.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions are met:
|
|
*
|
|
* 1. Redistributions of source code must retain the above copyright notice, this
|
|
* list of conditions and the following disclaimer.
|
|
*
|
|
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
|
* this list of conditions and the following disclaimer in the documentation
|
|
* and/or other materials provided with the distribution.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
|
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
|
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
|
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
|
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
*/
|
|
|
|
#pragma once
|
|
|
|
#include <AK/Assertions.h>
|
|
#include <AK/ByteBuffer.h>
|
|
#include <AK/HashMap.h>
|
|
#include <AK/IPv4Address.h>
|
|
#include <AK/MACAddress.h>
|
|
#include <AK/NetworkOrdered.h>
|
|
#include <AK/StringBuilder.h>
|
|
#include <AK/StringView.h>
|
|
#include <AK/Traits.h>
|
|
#include <AK/Types.h>
|
|
#include <string.h>
|
|
|
|
enum class DHCPv4Flags : u16 {
|
|
Broadcast = 1,
|
|
/* everything else is reserved and must be zero */
|
|
};
|
|
|
|
enum class DHCPv4Op : u8 {
|
|
BootRequest = 1,
|
|
BootReply = 2
|
|
};
|
|
|
|
enum class DHCPOption : u8 {
|
|
// BOOTP
|
|
Pad = 0,
|
|
SubnetMask,
|
|
TimeOffset,
|
|
Router,
|
|
TimeServer,
|
|
NameServer,
|
|
DomainNameServer,
|
|
LogServer,
|
|
CookieServer,
|
|
LPRServer,
|
|
ImpressServer,
|
|
ResourceLocationServer,
|
|
HostName,
|
|
BootFileSize,
|
|
MeritDumpFile,
|
|
DomainName,
|
|
SwapServer,
|
|
RootPath,
|
|
ExtensionsPath,
|
|
IPForwardingEnableDisable,
|
|
NonLocalSourceRoutingEnableDisable,
|
|
PolicyFilter,
|
|
MaximumDatagramReassemblySize,
|
|
DefaultIPTTL,
|
|
PathMTUAgingTimeout,
|
|
PathMTUPlateauTable,
|
|
InterfaceMTU,
|
|
AllSubnetsAreLocal,
|
|
BroadcastAddress,
|
|
PerformMaskDiscovery,
|
|
MaskSupplier,
|
|
PerformRouterDiscovery,
|
|
RouterSolicitationAddress,
|
|
StaticRoute,
|
|
TrailerEncapsulation,
|
|
ARPCacheTimeout,
|
|
EthernetEncapsulation,
|
|
TCPDefaultTTL,
|
|
TCPKeepaliveInterval,
|
|
TCPKeepaliveGarbage,
|
|
NetworkInformationServiceDomain,
|
|
NetworkInformationServers,
|
|
NetworkTimeProtocolServers,
|
|
VendorSpecificInformation,
|
|
NetBIOSOverTCPIPNameServer,
|
|
NetBIOSOverTCPIPDatagramDistributionServer,
|
|
NetBIOSOverTCPIPNodeType,
|
|
NetBIOSOverTCPIPScope,
|
|
XWindowSystemFontServer, // wow
|
|
XWindowSystemDisplayManager,
|
|
// DHCP
|
|
RequestedIPAddress = 50,
|
|
IPAddressLeaseTime,
|
|
OptionOverload,
|
|
DHCPMessageType,
|
|
ServerIdentifier,
|
|
ParameterRequestList,
|
|
Message,
|
|
MaximumDHCPMessageSize,
|
|
RenewalT1Time,
|
|
RenewalT2Time,
|
|
ClassIdentifier,
|
|
ClientIdentifier,
|
|
End = 255
|
|
};
|
|
|
|
enum class DHCPMessageType : u8 {
|
|
DHCPDiscover = 1,
|
|
DHCPOffer,
|
|
DHCPRequest,
|
|
DHCPDecline,
|
|
DHCPAck,
|
|
DHCPNak,
|
|
DHCPRelease,
|
|
};
|
|
|
|
template <>
|
|
struct AK::Traits<DHCPOption> : public AK::GenericTraits<DHCPOption> {
|
|
static constexpr bool is_trivial() { return true; }
|
|
static unsigned hash(DHCPOption u) { return int_hash((u8)u); }
|
|
};
|
|
|
|
struct ParsedDHCPv4Options {
|
|
template <typename T>
|
|
Optional<const T> get(DHCPOption option_name) const
|
|
{
|
|
auto option = options.get(option_name);
|
|
if (!option.has_value()) {
|
|
return {};
|
|
}
|
|
auto& value = option.value();
|
|
if (value.length != sizeof(T))
|
|
return {};
|
|
return *(const T*)value.value;
|
|
}
|
|
|
|
template <typename T>
|
|
Vector<T> get_many(DHCPOption option_name, size_t max_number) const
|
|
{
|
|
Vector<T> values;
|
|
|
|
auto option = options.get(option_name);
|
|
if (!option.has_value()) {
|
|
return {};
|
|
}
|
|
auto& value = option.value();
|
|
if (value.length < sizeof(T))
|
|
return {};
|
|
|
|
for (size_t i = 0; i < max_number; ++i) {
|
|
auto offset = i * sizeof(T);
|
|
if (offset >= value.length)
|
|
break;
|
|
values.append(*(T*)((u8*)const_cast<void*>(value.value) + offset));
|
|
}
|
|
|
|
return values;
|
|
}
|
|
|
|
String to_string() const
|
|
{
|
|
StringBuilder builder;
|
|
builder.append("DHCP Options (");
|
|
builder.appendf("%d", options.size());
|
|
builder.append(" entries)\n");
|
|
for (auto& opt : options) {
|
|
builder.appendf("\toption %d (%d bytes):", (u8)opt.key, (u8)opt.value.length);
|
|
for (auto i = 0; i < opt.value.length; ++i)
|
|
builder.appendf(" %u ", ((const u8*)opt.value.value)[i]);
|
|
builder.append('\n');
|
|
}
|
|
return builder.build();
|
|
}
|
|
|
|
struct DHCPOptionValue {
|
|
u8 length;
|
|
const void* value;
|
|
};
|
|
|
|
HashMap<DHCPOption, DHCPOptionValue> options;
|
|
};
|
|
|
|
constexpr auto DHCPV4_OPTION_FIELD_MAX_LENGTH = 312;
|
|
|
|
class [[gnu::packed]] DHCPv4Packet
|
|
{
|
|
public:
|
|
u8 op() const { return m_op; }
|
|
void set_op(DHCPv4Op op) { m_op = (u8)op; }
|
|
|
|
u8 htype() const { return m_htype; }
|
|
void set_htype(u8 htype) { m_htype = htype; }
|
|
|
|
u8 hlen() const { return m_hlen; }
|
|
void set_hlen(u8 hlen) { m_hlen = hlen; }
|
|
|
|
u8 hops() const { return m_hops; }
|
|
void set_hops(u8 hops) { m_hops = hops; }
|
|
|
|
u32 xid() const { return m_xid; }
|
|
void set_xid(u32 xid) { m_xid = xid; }
|
|
|
|
u16 secs() const { return m_secs; }
|
|
void set_secs(u16 secs) { m_secs = secs; }
|
|
|
|
u16 flags() const { return m_flags; }
|
|
void set_flags(DHCPv4Flags flags) { m_flags = (u16)flags; }
|
|
|
|
const IPv4Address& ciaddr() const { return m_ciaddr; }
|
|
const IPv4Address& yiaddr() const { return m_yiaddr; }
|
|
const IPv4Address& siaddr() const { return m_siaddr; }
|
|
const IPv4Address& giaddr() const { return m_giaddr; }
|
|
|
|
IPv4Address& ciaddr() { return m_ciaddr; }
|
|
IPv4Address& yiaddr() { return m_yiaddr; }
|
|
IPv4Address& siaddr() { return m_siaddr; }
|
|
IPv4Address& giaddr() { return m_giaddr; }
|
|
|
|
u8* options() { return m_options; }
|
|
ParsedDHCPv4Options parse_options() const;
|
|
|
|
const MACAddress& chaddr() const { return *(const MACAddress*)&m_chaddr[0]; }
|
|
void set_chaddr(const MACAddress& mac) { *(MACAddress*)&m_chaddr[0] = mac; }
|
|
|
|
StringView sname() const { return { (const char*)&m_sname[0] }; }
|
|
StringView file() const { return { (const char*)&m_file[0] }; }
|
|
|
|
private:
|
|
NetworkOrdered<u8> m_op;
|
|
NetworkOrdered<u8> m_htype;
|
|
NetworkOrdered<u8> m_hlen;
|
|
NetworkOrdered<u8> m_hops;
|
|
NetworkOrdered<u32> m_xid;
|
|
NetworkOrdered<u16> m_secs;
|
|
NetworkOrdered<u16> m_flags;
|
|
IPv4Address m_ciaddr;
|
|
IPv4Address m_yiaddr;
|
|
IPv4Address m_siaddr;
|
|
IPv4Address m_giaddr;
|
|
u8 m_chaddr[16]; // 10 bytes of padding at the end
|
|
u8 m_sname[64] { 0 };
|
|
u8 m_file[128] { 0 };
|
|
u8 m_options[DHCPV4_OPTION_FIELD_MAX_LENGTH] { 0 }; // variable, less than 312 bytes
|
|
};
|
|
|
|
class DHCPv4PacketBuilder {
|
|
public:
|
|
DHCPv4PacketBuilder()
|
|
: m_buffer(ByteBuffer::create_zeroed(sizeof(DHCPv4Packet)))
|
|
{
|
|
auto* options = peek().options();
|
|
// set the magic DHCP cookie value
|
|
options[0] = 99;
|
|
options[1] = 130;
|
|
options[2] = 83;
|
|
options[3] = 99;
|
|
}
|
|
|
|
void add_option(DHCPOption option, u8 length, const void* data)
|
|
{
|
|
ASSERT(m_can_add);
|
|
// we need enough space to fit the option value, its length, and its data
|
|
ASSERT(next_option_offset + length + 2 < DHCPV4_OPTION_FIELD_MAX_LENGTH);
|
|
|
|
auto* options = peek().options();
|
|
options[next_option_offset++] = (u8)option;
|
|
memcpy(options + next_option_offset, &length, 1);
|
|
next_option_offset++;
|
|
memcpy(options + next_option_offset, data, length);
|
|
next_option_offset += length;
|
|
}
|
|
|
|
void set_message_type(DHCPMessageType type) { add_option(DHCPOption::DHCPMessageType, 1, &type); }
|
|
|
|
DHCPv4Packet& peek() { return *(DHCPv4Packet*)m_buffer.data(); }
|
|
DHCPv4Packet& build()
|
|
{
|
|
add_option(DHCPOption::End, 0, nullptr);
|
|
m_can_add = false;
|
|
return *(DHCPv4Packet*)m_buffer.data();
|
|
}
|
|
size_t size() const { return m_buffer.size(); }
|
|
|
|
private:
|
|
ByteBuffer m_buffer;
|
|
size_t next_option_offset { 4 };
|
|
bool m_can_add { true };
|
|
};
|