155 lines
4.1 KiB
C++
155 lines
4.1 KiB
C++
|
/*
|
||
|
* Copyright (c) 2017 Jonas 'Sortie' Termansen.
|
||
|
*
|
||
|
* Permission to use, copy, modify, and distribute this software for any
|
||
|
* purpose with or without fee is hereby granted, provided that the above
|
||
|
* copyright notice and this permission notice appear in all copies.
|
||
|
*
|
||
|
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||
|
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||
|
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||
|
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||
|
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||
|
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||
|
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||
|
*
|
||
|
* net/lo/lo.cpp
|
||
|
* Loopback device.
|
||
|
*/
|
||
|
|
||
|
#include <sys/socket.h>
|
||
|
|
||
|
#include <arpa/inet.h>
|
||
|
#include <stdio.h>
|
||
|
|
||
|
#include <sortix/kernel/ioctx.h>
|
||
|
#include <sortix/kernel/kernel.h>
|
||
|
#include <sortix/kernel/kthread.h>
|
||
|
#include <sortix/kernel/if.h>
|
||
|
#include <sortix/kernel/worker.h>
|
||
|
|
||
|
#include "../ether.h"
|
||
|
|
||
|
#include "lo.h"
|
||
|
|
||
|
// The loopback device currently communicates through the Ethernet layer and
|
||
|
// pretends to do offload Ethernet checksumming as an optimization.
|
||
|
|
||
|
// The shared worker thread is used for processing. Whenever a packet needs to
|
||
|
// be sent, if the worker thread isn't scheduled, it is scheduled. The worker
|
||
|
// thread transmits all the packets that were in the queue when it begins, but
|
||
|
// not any more than that. If any work remains at the end, it schedules itself
|
||
|
// again to run later (to avoid starving other tasks using the shared worker
|
||
|
// thread). The packet queue is a singly linked list of packets.
|
||
|
|
||
|
namespace Sortix {
|
||
|
namespace Loopback {
|
||
|
|
||
|
class Loopback : public NetworkInterface
|
||
|
{
|
||
|
friend void Loopback__Recv(void* ctx);
|
||
|
|
||
|
public:
|
||
|
Loopback();
|
||
|
~Loopback();
|
||
|
|
||
|
public:
|
||
|
bool Send(Ref<Packet> pkt);
|
||
|
|
||
|
private:
|
||
|
void Recv();
|
||
|
|
||
|
private:
|
||
|
kthread_mutex_t socket_lock;
|
||
|
Ref<Packet> first_packet;
|
||
|
Ref<Packet> last_packet;
|
||
|
bool worker_scheduled;
|
||
|
|
||
|
};
|
||
|
|
||
|
Loopback::Loopback()
|
||
|
{
|
||
|
ifinfo.type = IF_TYPE_LOOPBACK;
|
||
|
ifinfo.features = IF_FEATURE_ETHERNET_CRC_OFFLOAD;
|
||
|
ifinfo.addrlen = 0;
|
||
|
ifstatus.flags = IF_STATUS_FLAGS_UP;
|
||
|
cfg.inet.address.s_addr = htobe32(INADDR_LOOPBACK);
|
||
|
cfg.inet.router.s_addr = htobe32(INADDR_ANY);
|
||
|
cfg.inet.subnet.s_addr = htobe32(INADDR_LOOPMASK);
|
||
|
socket_lock = KTHREAD_MUTEX_INITIALIZER;
|
||
|
// first_packet initialized by constructor
|
||
|
// last_packet initialized by constructor
|
||
|
worker_scheduled = false;
|
||
|
}
|
||
|
|
||
|
Loopback::~Loopback()
|
||
|
{
|
||
|
// Avoid stack overflow in first_packet recursive destructor.
|
||
|
while ( first_packet )
|
||
|
{
|
||
|
Ref<Packet> next = first_packet->next;
|
||
|
first_packet->next.Reset();
|
||
|
first_packet = next;
|
||
|
}
|
||
|
last_packet.Reset();
|
||
|
}
|
||
|
|
||
|
void Loopback__Recv(void* ctx)
|
||
|
{
|
||
|
((Loopback*) ctx)->Recv();
|
||
|
}
|
||
|
|
||
|
void Loopback::Recv()
|
||
|
{
|
||
|
kthread_mutex_lock(&socket_lock);
|
||
|
Ref<Packet> next_packet = first_packet;
|
||
|
first_packet.Reset();
|
||
|
last_packet.Reset();
|
||
|
kthread_mutex_unlock(&socket_lock);
|
||
|
while ( next_packet )
|
||
|
{
|
||
|
Ref<Packet> packet = next_packet;
|
||
|
next_packet = next_packet->next;
|
||
|
packet->next.Reset();
|
||
|
packet->netif = this;
|
||
|
Ether::Handle(packet, true);
|
||
|
}
|
||
|
kthread_mutex_lock(&socket_lock);
|
||
|
bool should_schedule = first_packet;
|
||
|
if ( !should_schedule )
|
||
|
worker_scheduled = false;
|
||
|
kthread_mutex_unlock(&socket_lock);
|
||
|
if ( should_schedule )
|
||
|
Worker::Schedule(Loopback__Recv, this);
|
||
|
}
|
||
|
|
||
|
bool Loopback::Send(Ref<Packet> pkt)
|
||
|
{
|
||
|
kthread_mutex_lock(&socket_lock);
|
||
|
if ( last_packet )
|
||
|
last_packet->next = pkt;
|
||
|
else
|
||
|
first_packet = pkt;
|
||
|
last_packet = pkt;
|
||
|
bool should_schedule = !worker_scheduled;
|
||
|
worker_scheduled = true;
|
||
|
kthread_mutex_unlock(&socket_lock);
|
||
|
if ( should_schedule )
|
||
|
Worker::Schedule(Loopback__Recv, this);
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
void Init(const char* /*devpath*/, Ref<Descriptor> dev)
|
||
|
{
|
||
|
Loopback* lo = new Loopback();
|
||
|
if ( !lo )
|
||
|
PanicF("Failed to allocate loopback device");
|
||
|
size_t index = 0;
|
||
|
snprintf(lo->ifinfo.name, sizeof(lo->ifinfo.name), "lo%zu", index);
|
||
|
if ( !RegisterNetworkInterface(lo, dev) )
|
||
|
PanicF("Failed to register %s as network interface", lo->ifinfo.name);
|
||
|
}
|
||
|
|
||
|
} // namespace Loopback
|
||
|
} // namespace Sortix
|