This repository has been archived by the owner on Sep 11, 2024. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 27
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
7 changed files
with
395 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,4 +1,5 @@ | ||
ADD_COMPILE_OPTIONS(-O2) | ||
ADD_SUBDIRECTORY(dhcpclient/) | ||
ADD_SUBDIRECTORY(init/) | ||
ADD_SUBDIRECTORY(pond/) | ||
ADD_SUBDIRECTORY(quack/) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
SET(SOURCES main.cpp Client.cpp DHCP.cpp) | ||
|
||
MAKE_PROGRAM(dhcpclient) | ||
TARGET_LINK_LIBRARIES(dhcpclient libduck) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,184 @@ | ||
/* SPDX-License-Identifier: GPL-3.0-or-later */ | ||
/* Copyright © 2016-2024 Byteduck */ | ||
|
||
#include "Client.h" | ||
#include "DHCP.h" | ||
#include <unistd.h> | ||
#include <ifaddrs.h> | ||
#include <libduck/Log.h> | ||
|
||
using namespace Duck; | ||
|
||
ResultRet<Ptr<Client>> Client::make() { | ||
// Get interfaces | ||
std::vector<Interface> interfaces; | ||
struct ifaddrs* addrs; | ||
if (getifaddrs(&addrs) < 0) | ||
return Result(errno); | ||
struct ifaddrs* cur_addr = addrs; | ||
while (cur_addr) { | ||
if (cur_addr->ifa_addr->sa_family != AF_INET || cur_addr->ifa_macaddr->sa_family != AF_MACADDR) | ||
goto next; | ||
|
||
interfaces.push_back(Interface { | ||
.name = cur_addr->ifa_name, | ||
.addr = { *((sockaddr_in*) cur_addr->ifa_addr) }, | ||
.hwaddr = { *((sockaddr_mac*) cur_addr->ifa_macaddr) } | ||
}); | ||
|
||
next: | ||
cur_addr = cur_addr->ifa_next; | ||
} | ||
freeifaddrs(addrs); | ||
|
||
if (interfaces.empty()) | ||
return Result("No interfaces to use"); | ||
|
||
// Open socket | ||
int fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); | ||
if (fd == -1) | ||
return Result(errno); | ||
|
||
// Bind to port 68 | ||
sockaddr_in addr = IPv4Address(0).as_sockaddr(68); | ||
if (bind(fd, (sockaddr*) &addr, sizeof(addr)) < 0) { | ||
close(fd); | ||
return Result(errno); | ||
} | ||
|
||
return Ptr<Client>(new Client(fd, interfaces)); | ||
} | ||
|
||
Client::Client(int socket, std::vector<Interface> interfaces): | ||
m_socket(socket), | ||
m_interfaces(std::move(interfaces)) | ||
{ | ||
} | ||
|
||
void Client::loop() { | ||
for (auto& interface : m_interfaces) | ||
discover(interface); | ||
|
||
DHCPPacket buf; | ||
while (true) { | ||
auto nread = recv(m_socket, &buf.raw_packet(), sizeof(RawDHCPPacket), 0); | ||
if (nread < 0) { | ||
Duck::Log::errf("Error reading packet: {}", strerror(errno)); | ||
break; | ||
} | ||
|
||
if (nread != sizeof(RawDHCPPacket)) { | ||
Duck::Log::errf("Received packet of invalid size: {}", nread); | ||
continue; | ||
} | ||
|
||
if (!buf.has_valid_cookie()) { | ||
Duck::Log::errf("Received packet with invalid magic cookie"); | ||
continue; | ||
} | ||
|
||
auto type = buf.get_option<uint8_t>(MessageType); | ||
if (!type.has_value()) { | ||
Duck::Log::errf("Received packet without message type"); | ||
continue; | ||
} | ||
|
||
Result res = Result::SUCCESS; | ||
switch (type.value()) { | ||
case Ack: | ||
res = do_ack(buf); | ||
break; | ||
|
||
case Offer: | ||
Duck::Log::warnf("Received offer, can't handle this yet"); | ||
break; | ||
|
||
case Nak: | ||
Duck::Log::warnf("Received nak, can't handle this yet"); | ||
break; | ||
|
||
case Decline: | ||
Duck::Log::warnf("Was declined DHCP request from {}", buf.raw_packet().siaddr); | ||
break; | ||
|
||
default: | ||
break; | ||
} | ||
|
||
if (res.is_error()) | ||
Duck::Log::errf("{}", res); | ||
} | ||
} | ||
|
||
Result Client::discover(const Interface& interface) { | ||
int txid = rand(); | ||
m_transactions[txid] = {.iface = interface, .id = txid}; | ||
|
||
DHCPPacket packet; | ||
packet.raw_packet().op = DHCPOp::BootPRequest; | ||
packet.raw_packet().htype = 1; | ||
packet.raw_packet().hlen = sizeof(MACAddress); | ||
packet.raw_packet().xid = txid; | ||
packet.raw_packet().flags = 0x1; | ||
packet.raw_packet().ciaddr = interface.addr; | ||
for (int i = 0; i < 6; i++) | ||
packet.raw_packet().chaddr[i] = interface.hwaddr[i]; | ||
packet.raw_packet().secs = 5000; | ||
|
||
uint8_t msgtype = DHCPMsgType::Discover; | ||
packet.add_option(MessageType, 1, &msgtype); | ||
packet.add_option(End, 0, nullptr); | ||
|
||
return send_packet(interface, packet.raw_packet()); | ||
} | ||
|
||
Result Client::send_packet(const Interface& interface, const RawDHCPPacket& packet) { | ||
const int sockid = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); | ||
if (sockid < 0) | ||
return errno; | ||
|
||
// Bind to interface | ||
if (setsockopt(sockid, SOL_SOCKET, SO_BINDTODEVICE, interface.name.c_str(), interface.name.length() + 1) < 0) { | ||
close(sockid); | ||
return errno; | ||
} | ||
|
||
// Set allow broadcast | ||
const int allow = 1; | ||
if (setsockopt(sockid, SOL_SOCKET, SO_BROADCAST, &allow, sizeof(int)) < 0) { | ||
close(sockid); | ||
return errno; | ||
} | ||
|
||
sockaddr_in addr = IPv4Address(255, 255, 255, 255).as_sockaddr(67); | ||
auto res = sendto(sockid, &packet, sizeof(packet), 0, (struct sockaddr*) &addr, sizeof(addr)); | ||
close(sockid); | ||
if (res < 0) | ||
return errno; | ||
|
||
return Result::SUCCESS; | ||
} | ||
|
||
Duck::Result Client::do_ack(const DHCPPacket& packet) { | ||
Duck::Log::dbgf("Received ack from {}", packet.raw_packet().siaddr); | ||
|
||
auto tx = m_transactions.find(packet.raw_packet().xid); | ||
if (tx == m_transactions.end()) | ||
return {"Couldn't handle ack: No such transaction"}; | ||
|
||
auto subnet = packet.get_option<IPv4Address>(SubnetMask); | ||
if (!subnet.has_value()) | ||
return {"Couldn't handle ack: Wasn't given a subnet mask"}; | ||
|
||
auto gateway = packet.get_option<IPv4Address>(Router); | ||
|
||
return setup_interface(tx->second.iface, packet.raw_packet().yiaddr, subnet.value(), gateway); | ||
} | ||
|
||
Duck::Result Client::setup_interface(const Client::Interface& interface, const IPv4Address& addr, const IPv4Address& subnet, const std::optional<IPv4Address>& gateway) { | ||
const int sockid = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); | ||
if (sockid < 0) | ||
return errno; | ||
|
||
// TODO | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,44 @@ | ||
/* SPDX-License-Identifier: GPL-3.0-or-later */ | ||
/* Copyright © 2016-2024 Byteduck */ | ||
|
||
#pragma once | ||
|
||
#include <string> | ||
#include <vector> | ||
#include "DHCP.h" | ||
#include <libduck/Result.h> | ||
#include <libduck/Object.h> | ||
#include <sys/socket.h> | ||
#include <map> | ||
|
||
class Client { | ||
public: | ||
static Duck::ResultRet<Duck::Ptr<Client>> make(); | ||
|
||
void loop(); | ||
|
||
private: | ||
struct Interface { | ||
std::string name; | ||
IPv4Address addr; | ||
MACAddress hwaddr; | ||
}; | ||
|
||
struct Transaction { | ||
Interface iface; | ||
int id; | ||
}; | ||
|
||
Client(int socket, std::vector<Interface> interfaces); | ||
|
||
Duck::Result discover(const Interface& interface); | ||
Duck::Result send_packet(const Interface& interface, const RawDHCPPacket& packet); | ||
|
||
Duck::Result do_ack(const DHCPPacket& packet); | ||
|
||
Duck::Result setup_interface(const Interface& interface, const IPv4Address& addr, const IPv4Address& subnet, const std::optional<IPv4Address>& gateway); | ||
|
||
std::vector<Interface> m_interfaces; | ||
int m_socket; | ||
std::map<int, Transaction> m_transactions; | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,43 @@ | ||
/* SPDX-License-Identifier: GPL-3.0-or-later */ | ||
/* Copyright © 2016-2024 Byteduck */ | ||
|
||
#include "DHCP.h" | ||
#include <string.h> | ||
|
||
DHCPPacket::DHCPPacket() { | ||
// Magic cookie | ||
m_packet.options[0] = 0x63; | ||
m_packet.options[1] = 0x82; | ||
m_packet.options[2] = 0x53; | ||
m_packet.options[3] = 0x63; | ||
} | ||
|
||
bool DHCPPacket::add_option(DHCPOption option, uint8_t size, const void* data) { | ||
if (m_options_offset + sizeof(uint8_t) * 2 + size > BOOTP_OPTS_MAXLEN) | ||
return false; | ||
m_packet.options[m_options_offset++] = option; | ||
m_packet.options[m_options_offset++] = size; | ||
if (data && size) { | ||
memcpy(&m_packet.options, data, size); | ||
m_options_offset += size; | ||
} | ||
return true; | ||
} | ||
|
||
bool DHCPPacket::has_valid_cookie() const { | ||
return m_packet.options[0] == 0x63 && m_packet.options[1] == 0x82 && m_packet.options[2] == 0x53 && m_packet.options[3] == 0x63; | ||
} | ||
|
||
bool DHCPPacket::get_option(DHCPOption option, size_t size, void* ptr) const { | ||
size_t offset = 4; | ||
while (offset < BOOTP_OPTS_MAXLEN) { | ||
if (!m_packet.options[offset] || !m_packet.options[offset + 1]) | ||
return false; | ||
if (m_packet.options[offset] == option && m_packet.options[offset + 1] == size) { | ||
memcpy(ptr, &m_packet.options[offset + 2], size); | ||
return true; | ||
} | ||
offset += m_packet.options[offset + 1] + 2; | ||
} | ||
return false; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,109 @@ | ||
/* SPDX-License-Identifier: GPL-3.0-or-later */ | ||
/* Copyright © 2016-2024 Byteduck */ | ||
|
||
#pragma once | ||
#include <kernel/api/endian.h> | ||
#include <kernel/api/ipv4.h> | ||
#include <kernel/api/net.h> | ||
#include <optional> | ||
|
||
enum DHCPMsgType { | ||
Discover = 1, | ||
Offer = 2, | ||
Request = 3, | ||
Decline = 4, | ||
Ack = 5, | ||
Nak = 6, | ||
Release = 7 | ||
}; | ||
|
||
enum DHCPOp { | ||
BootPRequest = 1, | ||
BootPReply = 2 | ||
}; | ||
|
||
enum DHCPOption { | ||
Pad = 0, | ||
SubnetMask = 1, | ||
TimeOffset = 2, | ||
Router = 3, | ||
TimeServer = 4, | ||
NameServer = 5, | ||
DomainNameServer = 6, | ||
LogServer = 7, | ||
CookieServer = 8, | ||
LPRServer = 9, | ||
ImpressServer = 10, | ||
ResourceLocationServer = 11, | ||
Hostname = 12, | ||
BootFileSize = 13, | ||
MeritDumpFile = 14, | ||
DomainName = 15, | ||
SwapServer = 16, | ||
RootPath = 17, | ||
ExtensionsPath = 18, | ||
RequestedIP = 50, | ||
IPLeaseTime = 51, | ||
OptionOverload = 52, | ||
MessageType = 53, | ||
ServerID = 54, | ||
ParameterRequestList = 55, | ||
Message = 56, | ||
MaximumMessageSize = 57, | ||
RenewalTime = 58, | ||
RebindingTime = 59, | ||
VendorClass = 60, | ||
ClientID = 61, | ||
TFTPServerName = 66, | ||
BootfileName = 67, | ||
End = 255 | ||
}; | ||
|
||
#define BOOTP_OPTS_MAXLEN 312 | ||
|
||
struct RawDHCPPacket { | ||
uint8_t op {0}; | ||
uint8_t htype {0}; | ||
uint8_t hlen {0}; | ||
uint8_t hop {0}; | ||
BigEndian<uint32_t> xid {0}; | ||
BigEndian<uint16_t> secs {0}; | ||
BigEndian<uint16_t> flags {0}; | ||
IPv4Address ciaddr {0}; | ||
IPv4Address yiaddr {0}; | ||
IPv4Address siaddr {0}; | ||
IPv4Address giaddr {0}; | ||
uint8_t chaddr[16] { 0 }; | ||
uint8_t sname[64] { 0 }; | ||
uint8_t file[128] { 0 }; | ||
uint8_t options[BOOTP_OPTS_MAXLEN] { 0 }; | ||
|
||
inline const MACAddress& mac() const { return *((const MACAddress*) &chaddr); } | ||
inline void set_mac(const MACAddress& mac) const { *((MACAddress*) &chaddr) = mac; } | ||
} __attribute__((packed)); | ||
|
||
class DHCPPacket { | ||
public: | ||
DHCPPacket(); | ||
explicit DHCPPacket(const RawDHCPPacket& packet): m_packet(packet) {} | ||
|
||
[[nodiscard]] RawDHCPPacket& raw_packet() { return m_packet; } | ||
[[nodiscard]] const RawDHCPPacket& raw_packet() const { return m_packet; } | ||
bool add_option(DHCPOption option, uint8_t size, const void* data); | ||
|
||
template<typename T> | ||
std::optional<T> get_option(DHCPOption option) const { | ||
T ret; | ||
if (get_option(option, sizeof(T), &ret)) | ||
return ret; | ||
return std::nullopt; | ||
} | ||
|
||
[[nodiscard]] bool has_valid_cookie() const; | ||
|
||
private: | ||
bool get_option(DHCPOption option, size_t size, void* ptr) const; | ||
|
||
RawDHCPPacket m_packet; | ||
size_t m_options_offset = 4; | ||
}; |
Oops, something went wrong.