xaizek / tos (License: GPLv3 only) (since 2018-12-07)
This is an alternative version of sources presented as part of Write Your Own OS video tutorial by Viktor Engelmann.
<root> / src / net / arp.cpp (fc8674371e637085425374a269e59d8436b4aebe) (3,047B) (mode 100644) [raw]
#include "net/arp.hpp"

#include "utils/conv.hpp"

using namespace net;

constexpr auto ethernetType = toNetOrder<std::uint16_t>(0x0001);
constexpr auto ip4Protocol = toNetOrder<std::uint16_t>(0x0800);
constexpr auto requestCmd = toNetOrder<std::uint16_t>(0x0001);
constexpr auto responseCmd = toNetOrder<std::uint16_t>(0x0002);

ARP::ARP(EtherFrameProvider &backend)
    : EtherFrameHandler(backend, 0x0806), numCacheEntries(0)
{
}

bool
ARP::onEtherFrameReceived(span<std::uint8_t> msg)
{
    if (msg.size() < ARPMsg::size()) {
        return false;
    }

    ARPMsg arp(msg.data());
    if (arp.hardwareType == ethernetType &&
        arp.protocol == ip4Protocol &&
        arp.hardwareAddressSize.BE == 6 &&
        arp.protocolAddressSize.BE == 4 &&
        arp.dstIP == backend.getIPAddress()) {

        switch (arp.command.BE) {
            case requestCmd.BE:
                arp.command = responseCmd;
                arp.dstIP = arp.srcIP;
                arp.dstMAC = arp.srcMAC;
                arp.srcIP = backend.getIPAddress();
                arp.srcMAC = backend.getMACAddress();
                return true;

            case responseCmd.BE:
                if (numCacheEntries < maxCacheEntries) {
                    IPcache[numCacheEntries] = arp.srcIP;
                    MACcache[numCacheEntries] = arp.srcMAC;
                    ++numCacheEntries;
                }
                break;
        }
    }

    return false;
}

void
ARP::requestMACAddress(NetOrder<std::uint32_t> ip)
{
    std::uint8_t data[ARPMsg::size()];

    ARPMsg arp(data);
    arp.hardwareType = ethernetType;
    arp.protocol = ip4Protocol;
    arp.hardwareAddressSize = NetOrder<std::uint8_t>(6); // mac
    arp.protocolAddressSize = NetOrder<std::uint8_t>(4); // ipv4
    arp.command = requestCmd;

    arp.srcMAC = backend.getMACAddress();
    arp.srcIP = backend.getIPAddress();
    arp.dstMAC = broadcastMAC;
    arp.dstIP = ip;

    send(arp.dstMAC, data);
}

void
ARP::broadcastMACAddress(NetOrder<std::uint32_t> ip)
{
    std::uint8_t data[ARPMsg::size()];

    ARPMsg arp(data);
    arp.hardwareType = ethernetType;
    arp.protocol = ip4Protocol;
    arp.hardwareAddressSize = NetOrder<std::uint8_t>(6); // mac
    arp.protocolAddressSize = NetOrder<std::uint8_t>(4); // ipv4
    arp.command = responseCmd;

    arp.srcMAC = backend.getMACAddress();
    arp.srcIP = backend.getIPAddress();
    arp.dstMAC = resolve(ip);
    arp.dstIP = ip;

    send(arp.dstMAC, data);
}

NetOrder<std::uint64_t>
ARP::getMACFromCache(NetOrder<std::uint32_t> ip)
{
    for (int i = 0; i < numCacheEntries; ++i) {
        if (IPcache[i] == ip) {
            return MACcache[i];
        }
    }
    return broadcastMAC;
}

NetOrder<std::uint64_t>
ARP::resolve(NetOrder<std::uint32_t> ip)
{
    NetOrder<std::uint64_t> result = getMACFromCache(ip);
    if (result == broadcastMAC) {
        requestMACAddress(ip);
    }

    // Possibly infinite loop.
    while (result == broadcastMAC) {
        result = getMACFromCache(ip);
    }

    return result;
}
Hints

Before first commit, do not forget to setup your git environment:
git config --global user.name "your_name_here"
git config --global user.email "your@email_here"

Clone this repository using HTTP(S):
git clone https://code.reversed.top/user/xaizek/tos

Clone this repository using ssh (do not forget to upload a key first):
git clone ssh://rocketgit@code.reversed.top/user/xaizek/tos

You are allowed to anonymously push to this repository.
This means that your pushed commits will automatically be transformed into a pull request:
... clone the repository ...
... make some changes and some commits ...
git push origin master