mirror of
https://gitlab.com/sortix/sortix.git
synced 2023-02-13 20:55:38 -05:00
140 lines
4.2 KiB
C++
140 lines
4.2 KiB
C++
|
/*
|
||
|
* Copyright (c) 2016, 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/ether.cpp
|
||
|
* Ethernet.
|
||
|
*/
|
||
|
|
||
|
#include <assert.h>
|
||
|
#include <errno.h>
|
||
|
#include <endian.h>
|
||
|
#include <netinet/if_ether.h>
|
||
|
#include <stdint.h>
|
||
|
#include <string.h>
|
||
|
|
||
|
#include <sortix/kernel/crc32.h>
|
||
|
#include <sortix/kernel/kernel.h>
|
||
|
#include <sortix/kernel/if.h>
|
||
|
#include <sortix/kernel/packet.h>
|
||
|
|
||
|
#include "arp.h"
|
||
|
#include "ether.h"
|
||
|
#include "ip.h"
|
||
|
|
||
|
namespace Sortix {
|
||
|
namespace Ether {
|
||
|
|
||
|
size_t GetMTU(NetworkInterface* netif)
|
||
|
{
|
||
|
(void) netif;
|
||
|
return ETHERMTU;
|
||
|
}
|
||
|
|
||
|
void Handle(Ref<Packet> pkt, bool checksum_offloaded)
|
||
|
{
|
||
|
assert(pkt->netif);
|
||
|
assert(pkt->offset <= pkt->length);
|
||
|
const unsigned char* in = pkt->from + pkt->offset;
|
||
|
size_t inlen = pkt->length - pkt->offset;
|
||
|
struct ether_header hdr;
|
||
|
if ( !checksum_offloaded )
|
||
|
{
|
||
|
struct ether_footer ftr;
|
||
|
if ( inlen < sizeof(hdr) + sizeof(ftr) /* ETHER_LEN */ )
|
||
|
return;
|
||
|
size_t ftr_offset = inlen - sizeof(ftr);
|
||
|
memcpy(&ftr, in + ftr_offset, sizeof(ftr));
|
||
|
pkt->length -= sizeof(ftr);
|
||
|
inlen -= sizeof(ftr);
|
||
|
ftr.ether_crc = le32toh(ftr.ether_crc);
|
||
|
if ( ftr.ether_crc != crc32(0, in, inlen) )
|
||
|
return;
|
||
|
}
|
||
|
else if ( inlen < sizeof(hdr) /* ETHER_HDR_LEN */ )
|
||
|
return;
|
||
|
memcpy(&hdr, in, sizeof(hdr));
|
||
|
hdr.ether_type = be16toh(hdr.ether_type);
|
||
|
pkt->offset += sizeof(hdr);
|
||
|
const struct ether_addr* src = (const struct ether_addr*) &hdr.ether_shost;
|
||
|
const struct ether_addr* dst = (const struct ether_addr*) &hdr.ether_dhost;
|
||
|
// Drop invalid frames with broadcast source.
|
||
|
if ( !memcmp(src, ðeraddr_broadcast, sizeof(struct ether_addr)) )
|
||
|
return;
|
||
|
// Accept only frames with destination being broadcast or our address.
|
||
|
bool dst_broadcast =
|
||
|
!memcmp(dst, ðeraddr_broadcast, sizeof(struct ether_addr));
|
||
|
if ( !dst_broadcast )
|
||
|
{
|
||
|
ScopedLock(&pkt->netif->cfg_lock);
|
||
|
const struct ether_addr* local = &pkt->netif->cfg.ether.address;
|
||
|
if ( pkt->netif->ifinfo.type != IF_TYPE_LOOPBACK &&
|
||
|
memcmp(dst, local, sizeof(struct ether_addr)) != 0 )
|
||
|
return;
|
||
|
}
|
||
|
switch ( hdr.ether_type )
|
||
|
{
|
||
|
case ETHERTYPE_IP: // IPv4
|
||
|
IP::Handle(pkt, src, dst, dst_broadcast);
|
||
|
break;
|
||
|
case ETHERTYPE_ARP: // Address Resolution Protocol
|
||
|
ARP::Handle(pkt, src, dst, dst_broadcast);
|
||
|
break;
|
||
|
case ETHERTYPE_IPV6: // IPv6
|
||
|
break;
|
||
|
default:
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
bool Send(Ref<Packet> pktin,
|
||
|
const struct ether_addr* src,
|
||
|
const struct ether_addr* dst,
|
||
|
uint16_t ether_type,
|
||
|
NetworkInterface* netif)
|
||
|
{
|
||
|
if ( ETHERMTU < pktin->length )
|
||
|
return errno = EMSGSIZE, false;
|
||
|
Ref<Packet> pkt = GetPacket();
|
||
|
if ( !pkt )
|
||
|
return false;
|
||
|
const unsigned char* in = pktin->from;
|
||
|
size_t inlen = pktin->length;
|
||
|
size_t padding = inlen < ETHERMIN ? ETHERMIN - inlen : 0;
|
||
|
unsigned char* out = pkt->from;
|
||
|
struct ether_header hdr;
|
||
|
struct ether_footer ftr;
|
||
|
size_t outlen = sizeof(hdr) /* ETHER_HDR_LEN */ + inlen + padding;
|
||
|
if ( !(netif->ifinfo.features & IF_FEATURE_ETHERNET_CRC_OFFLOAD) )
|
||
|
outlen += sizeof(ftr) /* ETHER_CRC_LEN */;
|
||
|
if ( pkt->pmap.size < outlen )
|
||
|
return errno = EMSGSIZE, false;
|
||
|
pkt->length = outlen;
|
||
|
memcpy(&hdr.ether_dhost, dst, sizeof(struct ether_addr));
|
||
|
memcpy(&hdr.ether_shost, src, sizeof(struct ether_addr));
|
||
|
hdr.ether_type = htobe16(ether_type);
|
||
|
memcpy(out, &hdr, sizeof(hdr));
|
||
|
memcpy(out + sizeof(hdr), in, inlen);
|
||
|
memset(out + sizeof(hdr) + inlen, 0, padding);
|
||
|
if ( !(netif->ifinfo.features & IF_FEATURE_ETHERNET_CRC_OFFLOAD) )
|
||
|
{
|
||
|
ftr.ether_crc = htole32(crc32(0, out, pkt->length));
|
||
|
memcpy(out + sizeof(hdr) + inlen + padding, &ftr, sizeof(ftr));
|
||
|
}
|
||
|
return netif->Send(pkt);
|
||
|
}
|
||
|
|
||
|
} // namespace Ether
|
||
|
} // namespace Sortix
|