diff --git a/api/fs/disk.hpp b/api/fs/disk.hpp index 0f8e857d6d..07ae90969e 100644 --- a/api/fs/disk.hpp +++ b/api/fs/disk.hpp @@ -15,6 +15,37 @@ // See the License for the specific language governing permissions and // limitations under the License. +/** + * /// Create basic FAT disk /// + * + * // create disk from a given disk-device + * auto disk = std::make_shared (device); + * // mount filesystem on auto-detected volume + * disk->mount( + * [disk] (fs::error_t err) { + * if (err) { + * printf("Bad!\n"); + * return; + * } + * // reference to filesystem + * auto& fs = disk->fs(); + * // synchronous stat: + * auto dirent = fs.stat("/file"); + * }); + * + * /// Construct custom filesystem /// + * + * // constructing on MBR means mount on sector 0 + * disk->mount(filesystem_args..., Disk::MBR, + * [disk] { + * printf("Disk mounted!\n"); + * auto& fs = disk->fs(); + * + * auto dirent = fs.stat("/file"); + * }); + * +**/ + #pragma once #ifndef FS_DISK_HPP #define FS_DISK_HPP @@ -31,30 +62,25 @@ namespace fs { class Disk { public: - struct Partition; //< Representation of a disk partition - - /** Callbacks */ + struct Partition; using on_parts_func = std::function&)>; using on_mount_func = std::function; + using lba_t = uint32_t; - /** Constructor */ - explicit Disk(hw::IDiskDevice&); - enum partition_t { MBR = 0, //< Master Boot Record (0) - /** extended partitions (1-4) */ - VBR1, + VBR1, //> extended partitions (1-4) VBR2, VBR3, VBR4, - + INVALID - }; //< enum partition_t - + }; + struct Partition { explicit Partition(const uint8_t fl, const uint8_t Id, - const uint32_t LBA, const uint32_t sz) noexcept : - flags {fl}, + const uint32_t LBA, const uint32_t sz) noexcept + : flags {fl}, id {Id}, lba_begin {LBA}, sectors {sz} @@ -78,33 +104,52 @@ namespace fs { }; //< struct Partition - /** Return a reference to the specified filesystem */ - FileSystem& fs() noexcept - { return *filesys; } - //************** disk functions **************// + // construct a disk with a given disk-device + explicit Disk(hw::IDiskDevice&); + + // returns the device the disk is using hw::IDiskDevice& dev() noexcept { return device; } - + // Returns true if the disk has no sectors bool empty() const noexcept { return device.size() == 0; } // Mounts the first partition detected (MBR -> VBR1-4 -> ext) + // NOTE: Always detects and instantiates a FAT filesystem void mount(on_mount_func func); - - // Mount partition @part as the filesystem FS + + // Mounts specified partition + // NOTE: Always detects and instantiates a FAT filesystem void mount(partition_t part, on_mount_func func); + + // mount custom filesystem on MBR or VBRn + template + void mount(Args&&... args, partition_t part, on_mount_func func) + { + // construct custom filesystem + filesys.reset(new T(args...)); + internal_mount(part, func); + } - /** - * Returns a vector of the partitions on a disk - * - * The disk does not need to be mounted beforehand - */ + // Creates a vector of the partitions on disk (see: on_parts_func) + // Note: The disk does not need to be mounted beforehand void partitions(on_parts_func func); - + + // returns true if a filesystem is mounted + bool fs_mounted() const noexcept + { return (bool) filesys; } + + // Returns a reference to a mounted filesystem + // If no filesystem is mounted, the results are undefined + FileSystem& fs() noexcept + { return *filesys; } + private: + void internal_mount(partition_t part, on_mount_func func); + hw::IDiskDevice& device; std::unique_ptr filesys; }; //< class Disk diff --git a/api/hw/pit.hpp b/api/hw/pit.hpp index 127da36b70..a730f50eed 100644 --- a/api/hw/pit.hpp +++ b/api/hw/pit.hpp @@ -97,7 +97,10 @@ namespace hw { /** Stop a timer. @param it: A valid iterator to timer */ void stop_timer(Timer_iterator it); - + + static void stop(Timer_iterator it) + { instance().stop_timer(it); } + inline size_t active_timers() { return timers_.size(); } /** No copy or move. The OS owns one instance forever. */ diff --git a/api/net/dhcp/dh4client.hpp b/api/net/dhcp/dh4client.hpp index c55bfa3b5f..b9f341ab8b 100644 --- a/api/net/dhcp/dh4client.hpp +++ b/api/net/dhcp/dh4client.hpp @@ -19,8 +19,8 @@ #ifndef NET_DHCP_DH4CLIENT_HPP #define NET_DHCP_DH4CLIENT_HPP +#include #include "../packet.hpp" -#include namespace net { @@ -33,28 +33,36 @@ namespace net { public: using Stack = Inet; - using On_config = delegate; + using config_func = delegate; DHClient() = delete; DHClient(DHClient&) = delete; - DHClient(Stack& inet) - : stack(inet) {} + DHClient(Stack& inet); - Stack& stack; - void negotiate(); // --> offer - inline void on_config(On_config handler){ - config_handler = handler; - } + // negotiate with local DHCP server + void negotiate(double timeout_secs); + + // handler for result of DHCP negotation + // timeout is true if the negotiation timed out + void on_config(config_func handler) + { config_handler = handler; } + + // disable or enable console spam + void set_silent(bool sil) + { this->console_spam = !sil; } private: void offer(UDPSocket&, const char* data, size_t len); void request(UDPSocket&); // --> acknowledge void acknowledge(const char* data, size_t len); - uint32_t xid; - IP4::addr ipaddr, netmask, router, dns_server; - uint32_t lease_time; - On_config config_handler = [](Stack&){ INFO("DHCPv4::On_config","Config complete"); }; + Stack& stack; + uint32_t xid; + IP4::addr ipaddr, netmask, router, dns_server; + uint32_t lease_time; + config_func config_handler; + hw::PIT::Timer_iterator timeout; + bool console_spam; }; } diff --git a/api/net/inet.hpp b/api/net/inet.hpp index 33b4823e25..348f100091 100644 --- a/api/net/inet.hpp +++ b/api/net/inet.hpp @@ -45,8 +45,6 @@ namespace net { virtual TCP& tcp() = 0; virtual UDP& udp() = 0; - virtual std::shared_ptr dhclient() = 0; - virtual constexpr uint16_t MTU() const = 0; virtual Packet_ptr createPacket(size_t size) = 0; diff --git a/api/net/inet4.hpp b/api/net/inet4.hpp index b90e04868e..79d1fe27cb 100644 --- a/api/net/inet4.hpp +++ b/api/net/inet4.hpp @@ -26,15 +26,15 @@ #include "ip4/arp.hpp" #include "ip4/ip4.hpp" #include "ip4/udp.hpp" +#include "ip4/icmpv4.hpp" #include "dns/client.hpp" #include "tcp.hpp" -#include "dhcp/dh4client.hpp" #include -#include "ip4/icmpv4.hpp" - namespace net { + class DHClient; + /** A complete IP4 network stack */ template class Inet4 : public Inet{ @@ -65,7 +65,7 @@ namespace net { UDP& udp() override { return udp_; } /** Get the DHCP client (if any) */ - inline std::shared_ptr dhclient() override { return dhcp_; } + auto dhclient() { return dhcp_; } /** Create a Packet, with a preallocated buffer. @param size : the "size" reported by the allocated packet. @@ -94,19 +94,22 @@ namespace net { * @func a delegate that provides a hostname and its address, which is 0 if the * name @hostname was not found. Note: Test with INADDR_ANY for a 0-address. **/ - inline virtual void - resolve(const std::string& hostname, - resolve_func func) override + virtual void resolve(const std::string& hostname, + resolve_func func) override { dns.resolve(this->dns_server, hostname, func); } - - inline virtual void - set_dns_server(IP4::addr server) override + + virtual void set_dns_server(IP4::addr server) override { this->dns_server = server; } - + + // handler called after the network successfully, or + // unsuccessfully negotiated with DHCP-server + // the timeout parameter indicates whether dhcp negotitation failed + void on_config(delegate handler); + /** We don't want to copy or move an IP-stack. It's tied to a device. */ Inet4(Inet4&) = delete; Inet4(Inet4&&) = delete; diff --git a/api/net/inet4.inc b/api/net/inet4.inc index 5b9e7e0a81..1363b60d56 100644 --- a/api/net/inet4.inc +++ b/api/net/inet4.inc @@ -1,6 +1,7 @@ //-*- C++ -*- #define DEBUG #include +#include "dhcp/dh4client.hpp" namespace net { @@ -25,7 +26,7 @@ namespace net /** Upstream wiring */ // Packets available nic.on_transmit_queue_available( - transmit_avail_delg::from, &Inet4::process_sendq>(*this)); + transmit_avail_delg::from, &Inet4::process_sendq>(*this)); // Phys -> Eth (Later, this will be passed through router) nic.set_linklayer_out(eth_bottom); @@ -82,9 +83,16 @@ namespace net { INFO("Inet4","Applying DHCP client"); dhcp_ = std::make_shared(*this); - dhcp_->negotiate(); + // 2 second timeout for DHCP-server negotation + dhcp_->negotiate(2.0); } - + + template + void Inet4::on_config(delegate handler) + { + dhcp_->on_config(handler); + } + template void Inet4::process_sendq(size_t packets) { diff --git a/src/debug/test_service.cpp b/src/debug/test_service.cpp index 49fc5b85cb..28d632c938 100644 --- a/src/debug/test_service.cpp +++ b/src/debug/test_service.cpp @@ -17,7 +17,6 @@ #include #include -#include #include "ircd.hpp" using namespace std::chrono; @@ -31,17 +30,17 @@ void Service::start() hw::Nic& eth0 = hw::Dev::eth<0,VirtioNet>(); inet = std::make_unique >(eth0); inet->network_config( - {{ 10,0,0,42 }}, // IP - {{ 255,255,255,0 }}, // Netmask - {{ 10,0,0,1 }}, // Gateway - {{ 8,8,8,8 }} ); // DNS + {{ 10,0,0,42 }}, // IP + {{ 255,255,255,0 }}, // Netmask + {{ 10,0,0,1 }}, // Gateway + {{ 8,8,8,8 }} ); // DNS /* - auto& tcp = inet->tcp(); - auto& server = tcp.bind(6667); // IRCd default port - server.onConnect( - [] (auto csock) - { + auto& tcp = inet->tcp(); + auto& server = tcp.bind(6667); // IRCd default port + server.onConnect( + [] (auto csock) + { printf("*** Received connection from %s\n", csock->remote().to_string().c_str()); @@ -70,43 +69,57 @@ void Service::start() /// inform others about disconnect //client.bcast(TK_QUIT, "Disconnected"); }); - });*/ - - using namespace net; - const UDP::port_t port = 4242; - auto& sock = inet->udp().bind(port); - - sock.on_read( - [&sock] (UDP::addr_t addr, UDP::port_t port, - const char* data, size_t len) - { - std::string strdata(data, len); - CHECK(1, "Getting UDP data from %s:%d -> %s", - addr.str().c_str(), port, strdata.c_str()); - // send the same thing right back! - sock.sendto(addr, port, data, len, - [&sock, addr, port] - { - // print this message once - printf("*** Starting spam (you should see this once)\n"); - - typedef std::function rnd_gen_t; - auto next = std::make_shared (); - - *next = - [next, &sock, addr, port] () - { - // spam this message at max speed - std::string text("Spamorino Cappucino\n"); - - sock.sendto(addr, port, text.data(), text.size(), - [next] { (*next)(); }); - }; - - // start spamming - (*next)(); - }); - }); - + });*/ + + inet->dhclient()->set_silent(true); + + inet->on_config( + [] (bool timeout) + { + if (timeout) + printf("Inet::on_config: Timeout\n"); + else + printf("Inet::on_config: DHCP Server acknowledged our request!\n"); + printf("Service IP address: %s, router: %s\n", + inet->ip_addr().str().c_str(), inet->router().str().c_str()); + + using namespace net; + const UDP::port_t port = 4242; + auto& sock = inet->udp().bind(port); + + sock.on_read( + [&sock] (UDP::addr_t addr, UDP::port_t port, + const char* data, size_t len) + { + std::string strdata(data, len); + CHECK(1, "Getting UDP data from %s:%d -> %s", + addr.str().c_str(), port, strdata.c_str()); + // send the same thing right back! + sock.sendto(addr, port, data, len, + [&sock, addr, port] + { + // print this message once + printf("*** Starting spam (you should see this once)\n"); + + typedef std::function rnd_gen_t; + auto next = std::make_shared (); + + *next = + [next, &sock, addr, port] () + { + // spam this message at max speed + std::string text("Spamorino Cappucino\n"); + + sock.sendto(addr, port, text.data(), text.size(), + [next] { (*next)(); }); + }; + + // start spamming + (*next)(); + }); + }); // sock on_read + + }); // on_config + printf("*** TEST SERVICE STARTED *** \n"); } diff --git a/src/fs/disk.cpp b/src/fs/disk.cpp index 00a6ea21b0..bfa2942fef 100644 --- a/src/fs/disk.cpp +++ b/src/fs/disk.cpp @@ -22,126 +22,134 @@ namespace fs { Disk::Disk(hw::IDiskDevice& dev) - : device {dev} -{ - // for now we can only assume FAT, anyways - filesys.reset(new FAT(device)); -} - + : device {dev} {} + void Disk::partitions(on_parts_func func) { /** Read Master Boot Record (sector 0) */ device.read(0, - [this, func] (hw::IDiskDevice::buffer_t data) - { - std::vector parts; - - if (!data) { - func(true, parts); - return; - } - - // First sector is the Master Boot Record - auto* mbr =(MBR::mbr*) data.get(); - - for (int i {0}; i < 4; ++i) { - // all the partitions are offsets to potential Volume Boot Records - parts.emplace_back( - mbr->part[i].flags, //< flags - mbr->part[i].type, //< id - mbr->part[i].lba_begin, //< LBA - mbr->part[i].sectors); - } - - func(no_error, parts); - }); + [this, func] (hw::IDiskDevice::buffer_t data) + { + std::vector parts; + + if (!data) { + func(true, parts); + return; + } + + // First sector is the Master Boot Record + auto* mbr =(MBR::mbr*) data.get(); + + for (int i {0}; i < 4; ++i) { + // all the partitions are offsets to potential Volume Boot Records + parts.emplace_back( + mbr->part[i].flags, //< flags + mbr->part[i].type, //< id + mbr->part[i].lba_begin, //< LBA + mbr->part[i].sectors); + } + + func(no_error, parts); + }); } void Disk::mount(on_mount_func func) { device.read(0, - [this, func] (hw::IDiskDevice::buffer_t data) - { - if (!data) { - // TODO: error-case for unable to read MBR - mount(INVALID, func); - return; - } - - // auto-detect FAT on MBR: - auto* mbr = (MBR::mbr*) data.get(); - MBR::BPB* bpb = mbr->bpb(); - - if (bpb->bytes_per_sector >= 512 - && bpb->fa_tables != 0 - && bpb->signature != 0) // check MBR signature too - { - // we have FAT on MBR (and we are assuming mount FAT) - mount(MBR, func); - return; - } - - // go through partition list - for (int i = 0; i < 4; i++) - { - if (mbr->part[i].type != 0 // 0 is unused partition - && mbr->part[i].lba_begin != 0 // 0 is MBR anyways - && mbr->part[i].sectors != 0) // 0 means no size, so... - { - mount((partition_t) (VBR1 + i), func); - return; - } - } - - // no partition was found (TODO: extended partitions) - mount(INVALID, func); - return; - }); - } + [this, func] (hw::IDiskDevice::buffer_t data) + { + if (!data) { + // TODO: error-case for unable to read MBR + internal_mount(INVALID, func); + return; + } - void Disk::mount(partition_t part, on_mount_func func) { - - if (part == INVALID) + // auto-detect FAT on MBR: + auto* mbr = (MBR::mbr*) data.get(); + MBR::BPB* bpb = mbr->bpb(); + + if (bpb->bytes_per_sector >= 512 + && bpb->fa_tables != 0 + && bpb->signature != 0) // check MBR signature too { - // Something bad happened maybe in auto-detect - // Either way, no partition was found - func(true); + // detected FAT on MBR + filesys.reset(new FAT(device)); + // mount on MBR + internal_mount(MBR, func); return; } - else if (part == MBR) + + // go through partition list + for (int i = 0; i < 4; i++) { - // For the MBR case, all we need to do is mount on sector 0 - fs().mount(0, device.size(), func); + if (mbr->part[i].type != 0 // 0 is unused partition + && mbr->part[i].lba_begin != 0 // 0 is MBR anyways + && mbr->part[i].sectors != 0) // 0 means no size, so... + { + // FIXME: for now we can only assume FAT, anyways + // To be replaced with lookup table for partition identifiers, + // but we really only have FAT atm, so its just wasteful + filesys.reset(new FAT(device)); + // mount on VBRn + internal_mount((partition_t) (VBR1 + i), func); + return; + } } + + // no partition was found (TODO: extended partitions) + internal_mount(INVALID, func); + }); + } + + void Disk::mount(partition_t part, on_mount_func func) + { + filesys.reset(new FAT(device)); + internal_mount(part, func); + } + + void Disk::internal_mount(partition_t part, on_mount_func func) { + + if (part == INVALID) + { + // Something bad happened maybe in auto-detect + // Either way, no partition was found + func(true); + return; + } + else if (part == MBR) + { + // For the MBR case, all we need to do is mount on sector 0 + fs().mount(0, device.size(), func); + } else + { + /** + * Otherwise, we will have to read the LBA offset + * of the partition to be mounted + */ + device.read(0, + [this, part, func] (hw::IDiskDevice::buffer_t data) { + if (!data) { + // TODO: error-case for unable to read MBR + func(true); + return; + } + + auto* mbr = (MBR::mbr*) data.get(); //< Treat data as MBR + auto pint = static_cast(part - 1); //< Treat VBR1 as index 0 etc. + + /** Get LBA from selected partition */ + auto lba_base = mbr->part[pint].lba_begin; + auto lba_size = mbr->part[pint].sectors; + /** - * Otherwise, we will have to read the LBA offset - * of the partition to be mounted + * Call the filesystems mount function + * with lba_begin as base address */ - device.read(0, - [this, part, func] (hw::IDiskDevice::buffer_t data) - { - if (!data) { - // TODO: error-case for unable to read MBR - func(true); - return; - } - - auto* mbr = (MBR::mbr*) data.get(); //< Treat data as MBR - auto pint = static_cast(part - 1); //< Treat VBR1 as index 0 etc. - - /** Get LBA from selected partition */ - auto lba_base = mbr->part[pint].lba_begin; - auto lba_size = mbr->part[pint].sectors; - - /** - * Call the filesystems mount function - * with lba_begin as base address - */ - fs().mount(lba_base, lba_size, func); - }); - } + fs().mount(lba_base, lba_size, func); + }); + } } std::string Disk::Partition::name() const { diff --git a/src/kernel/terminal_disk.cpp b/src/kernel/terminal_disk.cpp index 62db898cfd..ad2446fd8e 100644 --- a/src/kernel/terminal_disk.cpp +++ b/src/kernel/terminal_disk.cpp @@ -45,116 +45,116 @@ void Terminal::add_disk_commands(Disk_ptr disk) // add 'cd' command add("cd", "Change current directory", - [this, curdir, disk] (const std::vector& args) -> int - { - // current directory, somehow... - std::string target = "/"; - if (!args.empty()) target = args[0]; - - Path path(*curdir); - path += target; - - int rv = target_directory(disk, path); - if (rv) - { - this->write("cd: %s: No such file or directory\r\n", target.c_str()); - return rv; - } - *curdir = path.to_string(); - return 0; - }); + [this, curdir, disk] (const std::vector& args) -> int + { + // current directory, somehow... + std::string target = "/"; + if (!args.empty()) target = args[0]; + + Path path(*curdir); + path += target; + + int rv = target_directory(disk, path); + if (rv) + { + this->write("cd: %s: No such file or directory\r\n", target.c_str()); + return rv; + } + *curdir = path.to_string(); + return 0; + }); // add 'ls' command add("ls", "List files in a folder", - [this, curdir, disk] (const std::vector& args) -> int + [this, curdir, disk] (const std::vector& args) -> int + { + // current directory, somehow... + Path path(*curdir); + if (!args.empty()) path += args[0]; + + int rv = target_directory(disk, path); + if (rv) + { + this->write("ls: %s: No such file or directory\r\n", path.to_string().c_str()); + return rv; + } + + std::string target = path.to_string(); + + auto& fs = disk->fs(); + auto vec = fs::new_shared_vector(); + auto err = fs.ls(target, vec); + if (!err) + { + this->write("%s \t%s \t%s \t%s\r\n", + "Name", "Size", "Type", "Sector"); + for (auto& ent : *vec) { - // current directory, somehow... - Path path(*curdir); - if (!args.empty()) path += args[0]; - - int rv = target_directory(disk, path); - if (rv) - { - this->write("ls: %s: No such file or directory\r\n", path.to_string().c_str()); - return rv; - } - - std::string target = path.to_string(); - - auto& fs = disk->fs(); - auto vec = fs::new_shared_vector(); - auto err = fs.ls(target, vec); - if (!err) - { - this->write("%s \t%s \t%s \t%s\r\n", - "Name", "Size", "Type", "Sector"); - for (auto& ent : *vec) - { - this->write("%s \t%llu \t%s \t%llu\r\n", - ent.name().c_str(), ent.size, ent.type_string().c_str(), ent.block); - } - this->write("Total %u\r\n", vec->size()); - return 0; - } - else - { - this->write("Could not list %s\r\n", args[0].c_str()); - return 1; - } - }); + this->write("%s \t%llu \t%s \t%llu\r\n", + ent.name().c_str(), ent.size, ent.type_string().c_str(), ent.block); + } + this->write("Total %u\r\n", vec->size()); + return 0; + } + else + { + this->write("Could not list %s\r\n", args[0].c_str()); + return 1; + } + }); // add 'stat' command add("stat", "List file information", - [this, disk] (const std::vector& args) -> int + [this, disk] (const std::vector& args) -> int + { + if (!args.empty()) + { + auto& fs = disk->fs(); + auto ent = fs.stat(args[0]); + if (ent.is_valid()) + { + this->write("%s \t%s \t%s \t%s\r\n", + "Name", "Size", "Type", "Sector"); + this->write("%s \t%llu \t%s \t%llu\r\n", + ent.name().c_str(), ent.size, ent.type_string().c_str(), ent.block); + return 0; + } + else { - if (!args.empty()) - { - auto& fs = disk->fs(); - auto ent = fs.stat(args[0]); - if (ent.is_valid()) - { - this->write("%s \t%s \t%s \t%s\r\n", - "Name", "Size", "Type", "Sector"); - this->write("%s \t%llu \t%s \t%llu\r\n", - ent.name().c_str(), ent.size, ent.type_string().c_str(), ent.block); - return 0; - } - else - { - this->write("stat: Cannot stat '%s'\r\n", args[0].c_str()); - return 1; - } - } - else - { - this->write("%s\r\n", "stat: Not enough arguments"); - return 1; - } - }); + this->write("stat: Cannot stat '%s'\r\n", args[0].c_str()); + return 1; + } + } + else + { + this->write("%s\r\n", "stat: Not enough arguments"); + return 1; + } + }); // add 'cat' command add("cat", "Concatenate files and print", - [this, disk] (const std::vector& args) -> int + [this, disk] (const std::vector& args) -> int + { + auto& fs = disk->fs(); + + for (const auto& file : args) + { + // get file information + auto ent = fs.stat(file); + if (!ent.is_valid()) { - auto& fs = disk->fs(); - - for (const auto& file : args) - { - // get file information - auto ent = fs.stat(file); - if (!ent.is_valid()) - { - this->write("cat: '%s': No such file or directory\r\n", file.c_str()); - return 1; - } - // read file contents - auto buf = fs.read(ent, 0, ent.size); - if (!buf.buffer) - { - this->write("cat: '%s': I/O error\r\n", file.c_str()); - return 1; - } - // write to terminal client - std::string buffer((char*) buf.buffer.get(), buf.len); - this->write("%s\r\n", buffer.c_str()); - } - return 0; - }); + this->write("cat: '%s': No such file or directory\r\n", file.c_str()); + return 1; + } + // read file contents + auto buf = fs.read(ent, 0, ent.size); + if (!buf.buffer) + { + this->write("cat: '%s': I/O error\r\n", file.c_str()); + return 1; + } + // write to terminal client + std::string buffer((char*) buf.buffer.get(), buf.len); + this->write("%s\r\n", buffer.c_str()); + } + return 0; + }); } diff --git a/src/net/dhcp/dh4client.cpp b/src/net/dhcp/dh4client.cpp index 8d23d662d1..d430f6e000 100644 --- a/src/net/dhcp/dh4client.cpp +++ b/src/net/dhcp/dh4client.cpp @@ -154,12 +154,37 @@ namespace net { return (dhcp_option_t*) option; } - - void DHClient::negotiate() + + DHClient::DHClient(Stack& inet) + : stack(inet), xid(0), console_spam(true) + { + config_handler = + [this] (bool timeout) { + if (console_spam) + { + if (timeout) + INFO("DHCPv4","Negotiation timed out"); + else + INFO("DHCPv4","Config complete"); + } + }; + } + + void DHClient::negotiate(double timeout_secs) { + // set timeout handler + this->timeout = hw::PIT::instance().on_timeout(timeout_secs, + [this] { + // reset session ID + this->xid = 0; + // call on_config with timeout = true + this->config_handler(true); + }); + // create a random session ID this->xid = OS::cycles_since_boot() & 0xFFFFFFFF; - MYINFO("Negotiating IP-address (xid=%u)", xid); + if (console_spam) + MYINFO("Negotiating IP-address (xid=%u)", xid); // create DHCP discover packet const size_t packetlen = sizeof(dhcp_packet_t); @@ -220,17 +245,17 @@ namespace net socket.bcast(IP4::INADDR_ANY, DHCP_DEST_PORT, packet, packetlen); socket.on_read( - [this, &socket] (IP4::addr, UDP::port_t port, - const char* data, size_t len) - { - if (port == DHCP_DEST_PORT) - { - // we have got a DHCP Offer - debug("Received possible DHCP OFFER from %s:%d\n", - addr.str().c_str(), DHCP_DEST_PORT); - this->offer(socket, data, len); - } - }); + [this, &socket] (IP4::addr, UDP::port_t port, + const char* data, size_t len) + { + if (port == DHCP_DEST_PORT) + { + // we have got a DHCP Offer + debug("Received possible DHCP OFFER from %s:%d\n", + addr.str().c_str(), DHCP_DEST_PORT); + this->offer(socket, data, len); + } + }); } const dhcp_option_t* get_option(const uint8_t* options, uint8_t code) @@ -257,70 +282,72 @@ namespace net opt = get_option(dhcp->options, DHO_DHCP_MESSAGE_TYPE); if (opt->code == DHO_DHCP_MESSAGE_TYPE) - { - // verify that the type is indeed DHCPOFFER - debug("Found DHCP message type %d (DHCP Offer = %d)\n", - opt->val[0], DHCPOFFER); - - // ignore when not a DHCP Offer - if (opt->val[0] != DHCPOFFER) return; - } + { + // verify that the type is indeed DHCPOFFER + debug("Found DHCP message type %d (DHCP Offer = %d)\n", + opt->val[0], DHCPOFFER); + + // ignore when not a DHCP Offer + if (opt->val[0] != DHCPOFFER) return; + } // ignore message when DHCP message type is missing else return; // the offered IP address: this->ipaddr = dhcp->yiaddr; - MYINFO("IP ADDRESS: \t%s", - this->ipaddr.str().c_str()); - + if (console_spam) + MYINFO("IP ADDRESS: \t%s", this->ipaddr.str().c_str()); + opt = get_option(dhcp->options, DHO_SUBNET_MASK); if (opt->code == DHO_SUBNET_MASK) - { - memcpy(&this->netmask, opt->val, sizeof(IP4::addr)); - MYINFO("SUBNET MASK: \t%s", - this->netmask.str().c_str()); - } - + { + memcpy(&this->netmask, opt->val, sizeof(IP4::addr)); + if (console_spam) + MYINFO("SUBNET MASK: \t%s", this->netmask.str().c_str()); + } + opt = get_option(dhcp->options, DHO_DHCP_LEASE_TIME); if (opt->code == DHO_DHCP_LEASE_TIME) - { - memcpy(&this->lease_time, opt->val, sizeof(this->lease_time)); + { + memcpy(&this->lease_time, opt->val, sizeof(this->lease_time)); + if (console_spam) MYINFO("LEASE TIME: \t%u mins", this->lease_time / 60); - } - + } + // now validate the offer, checking for minimum information opt = get_option(dhcp->options, DHO_ROUTERS); if (opt->code == DHO_ROUTERS) - { - memcpy(&this->router, opt->val, sizeof(IP4::addr)); - MYINFO("GATEWAY: \t%s", - this->router.str().c_str()); - } + { + memcpy(&this->router, opt->val, sizeof(IP4::addr)); + if (console_spam) + MYINFO("GATEWAY: \t%s", this->router.str().c_str()); + } // assume that the server we received the request from is the gateway else + { + opt = get_option(dhcp->options, DHO_DHCP_SERVER_IDENTIFIER); + if (opt->code == DHO_DHCP_SERVER_IDENTIFIER) { - opt = get_option(dhcp->options, DHO_DHCP_SERVER_IDENTIFIER); - if (opt->code == DHO_DHCP_SERVER_IDENTIFIER) - { - memcpy(&this->router, opt->val, sizeof(IP4::addr)); - MYINFO("GATEWAY: \t%s", - this->router.str().c_str()); - } - // silently ignore when both ROUTER and SERVER_ID is missing - else return; + memcpy(&this->router, opt->val, sizeof(IP4::addr)); + if (console_spam) + MYINFO("GATEWAY: \t%s", this->router.str().c_str()); } - + // silently ignore when both ROUTER and SERVER_ID is missing + else return; + } + opt = get_option(dhcp->options, DHO_DOMAIN_NAME_SERVERS); if (opt->code == DHO_DOMAIN_NAME_SERVERS) - { - memcpy(&this->dns_server, opt->val, sizeof(IP4::addr)); - } + { + memcpy(&this->dns_server, opt->val, sizeof(IP4::addr)); + } else - { // just try using ROUTER as DNS server - this->dns_server = this->router; - } - MYINFO("DNS SERVER: \t%s", this->dns_server.str().c_str()); - + { // just try using ROUTER as DNS server + this->dns_server = this->router; + } + if (console_spam) + MYINFO("DNS SERVER: \t%s", this->dns_server.str().c_str()); + // we can accept the offer now by requesting the IP! this->request(sock); } @@ -393,18 +420,18 @@ namespace net // set our onRead function to point to a hopeful DHCP ACK! sock.on_read( - [this] (IP4::addr, UDP::port_t port, - const char* data, size_t len) - { - if (port == DHCP_DEST_PORT) - { - // we have hopefully got a DHCP Ack - debug("\tReceived DHCP ACK from %s:%d\n", - addr.str().c_str(), DHCP_DEST_PORT); - this->acknowledge(data, len); - } - }); - + [this] (IP4::addr, UDP::port_t port, + const char* data, size_t len) + { + if (port == DHCP_DEST_PORT) + { + // we have hopefully got a DHCP Ack + debug("\tReceived DHCP ACK from %s:%d\n", + addr.str().c_str(), DHCP_DEST_PORT); + this->acknowledge(data, len); + } + }); + // send our DHCP Request sock.bcast(IP4::INADDR_ANY, DHCP_DEST_PORT, packet, packetlen); } @@ -422,22 +449,25 @@ namespace net opt = get_option(dhcp->options, DHO_DHCP_MESSAGE_TYPE); if (opt->code == DHO_DHCP_MESSAGE_TYPE) - { - // verify that the type is indeed DHCPOFFER - debug("\tFound DHCP message type %d (DHCP Ack = %d)\n", - opt->val[0], DHCPACK); - - // ignore when not a DHCP Offer - if (opt->val[0] != DHCPACK) return; - } + { + // verify that the type is indeed DHCPOFFER + debug("\tFound DHCP message type %d (DHCP Ack = %d)\n", + opt->val[0], DHCPACK); + // ignore when not a DHCP Offer + if (opt->val[0] != DHCPACK) return; + } // ignore message when DHCP message type is missing else return; + if (console_spam) + MYINFO("Server acknowledged our request!"); + // configure our network stack - MYINFO("Server acknowledged our request!"); stack.network_config(this->ipaddr, this->netmask, this->router, this->dns_server); + // stop timeout from happening + hw::PIT::stop(timeout); // run some post-DHCP event to release the hounds - this->config_handler(stack); + this->config_handler(false); } } diff --git a/test/fat/fat16.cpp b/test/fat/fat16.cpp index 23d11c8317..2ce4bc481f 100644 --- a/test/fat/fat16.cpp +++ b/test/fat/fat16.cpp @@ -38,69 +38,69 @@ void Service::start() // auto-mount filesystem disk->mount( - [disk] (fs::error_t err) - { - CHECKSERT(!err, "Filesystem auto-mounted"); + [disk] (fs::error_t err) + { + CHECKSERT(!err, "Filesystem auto-mounted"); - auto& fs = disk->fs(); - printf("\t\t%s filesystem\n", fs.name().c_str()); + auto& fs = disk->fs(); + printf("\t\t%s filesystem\n", fs.name().c_str()); - auto vec = fs::new_shared_vector(); - err = fs.ls("/", vec); - CHECKSERT(!err, "List root directory"); + auto vec = fs::new_shared_vector(); + err = fs.ls("/", vec); + CHECKSERT(!err, "List root directory"); - CHECKSERT(vec->size() == 1, "Exactly one ent in root dir"); + CHECKSERT(vec->size() == 1, "Exactly one ent in root dir"); - auto& e = vec->at(0); - CHECKSERT(e.is_file(), "Ent is a file"); - CHECKSERT(e.name() == "banana.txt", "Ents name is 'banana.txt'"); + auto& e = vec->at(0); + CHECKSERT(e.is_file(), "Ent is a file"); + CHECKSERT(e.name() == "banana.txt", "Ents name is 'banana.txt'"); - }); + }); // re-mount on VBR1 disk->mount(disk->VBR1, - [disk] (fs::error_t err) - { - CHECKSERT(!err, "Filesystem mounted on VBR1"); - - // verify that we can read file - auto& fs = disk->fs(); - auto ent = fs.stat("/banana.txt"); - CHECKSERT(ent.is_valid(), "Stat file in root dir"); - CHECKSERT(ent.is_file(), "Entity is file"); - CHECKSERT(!ent.is_dir(), "Entity is not directory"); - CHECKSERT(ent.name() == "banana.txt", "Name is 'banana.txt'"); - - printf("%s\n", internal_banana.c_str()); - - // try reading banana-file - auto buf = fs.read(ent, 0, ent.size); - auto banana = buf.to_string(); - - CHECKSERT(banana == internal_banana, "Correct banana #1"); - - bool test = true; - - for (size_t i = 0; i < internal_banana.size(); i++) - { - // read one byte at a time - buf = fs.read(ent, i, 1); - /// @buf should evaluate to 'true' if its valid - CHECKSERT(buf, "Validate buffer"); - - // verify that it matches the same location in test-string - test = ((char) buf.buffer.get()[0] == internal_banana[i]); - if (!test) - { - printf("!! Random access read test failed on i = %u\n", i); - break; - } - } - CHECKSERT(test, "Validate random access sync read"); - - buf = fs.readFile("/banana.txt"); - banana = buf.to_string(); - CHECKSERT(banana == internal_banana, "Correct banana #2"); - }); + [disk] (fs::error_t err) + { + CHECKSERT(!err, "Filesystem mounted on VBR1"); + + // verify that we can read file + auto& fs = disk->fs(); + auto ent = fs.stat("/banana.txt"); + CHECKSERT(ent.is_valid(), "Stat file in root dir"); + CHECKSERT(ent.is_file(), "Entity is file"); + CHECKSERT(!ent.is_dir(), "Entity is not directory"); + CHECKSERT(ent.name() == "banana.txt", "Name is 'banana.txt'"); + + printf("%s\n", internal_banana.c_str()); + + // try reading banana-file + auto buf = fs.read(ent, 0, ent.size); + auto banana = buf.to_string(); + + CHECKSERT(banana == internal_banana, "Correct banana #1"); + + bool test = true; + + for (size_t i = 0; i < internal_banana.size(); i++) + { + // read one byte at a time + buf = fs.read(ent, i, 1); + /// @buf should evaluate to 'true' if its valid + CHECKSERT(buf, "Validate buffer"); + + // verify that it matches the same location in test-string + test = ((char) buf.buffer.get()[0] == internal_banana[i]); + if (!test) + { + printf("!! Random access read test failed on i = %u\n", i); + break; + } + } + CHECKSERT(test, "Validate random access sync read"); + + buf = fs.readFile("/banana.txt"); + banana = buf.to_string(); + CHECKSERT(banana == internal_banana, "Correct banana #2"); + }); INFO("FAT16", "SUCCESS"); }