/* * 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 #include #include #include #include #include #include #include #include #include #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 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 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 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