open5gs/src/upf/arp-nd.cpp

104 lines
2.9 KiB
C++

/*
* Copyright (C) 2019 by Sukchan Lee <acetcom@gmail.com>
*
* This file is part of Open5GS.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#include <tins/arp.h>
#include <tins/ethernetII.h>
#include <tins/hw_address.h>
#include <tins/icmpv6.h>
#include "arp-nd.h"
using namespace::Tins;
void _serialize_reply(uint8_t *reply_data, EthernetII &reply)
{
PDU::serialization_type serialized = reply.serialize();
memcpy(reply_data, serialized.data(), reply.size());
}
bool _parse_arp(EthernetII &pdu)
{
if (pdu.payload_type() == ETHERTYPE_ARP) {
const ARP& arp = pdu.rfind_pdu<ARP>();
return arp.opcode() == ARP::REQUEST && pdu.dst_addr().is_broadcast();
}
return false;
}
bool is_arp_req(uint8_t *data, uint len)
{
EthernetII pdu(data, len);
return _parse_arp(pdu);
}
bool arp_reply(uint8_t *reply_data, uint8_t *request_data, uint len,
const uint8_t *mac)
{
EthernetII pdu(request_data, len);
if (_parse_arp(pdu)) {
HWAddress<ETHER_ADDR_LEN> source_mac(mac);
const ARP& arp = pdu.rfind_pdu<ARP>();
EthernetII reply = ARP::make_arp_reply(
arp.sender_ip_addr(),
arp.target_ip_addr(),
arp.sender_hw_addr(),
source_mac);
_serialize_reply(reply_data, reply);
return true;
}
return false;
}
bool _parse_nd(EthernetII &pdu)
{
if (pdu.payload_type() == ETHERTYPE_IPV6) {
const ICMPv6& icmp6 = pdu.rfind_pdu<ICMPv6>();
return icmp6.type() == ICMPv6::NEIGHBOUR_SOLICIT;
}
return false;
}
bool is_nd_req(uint8_t *data, uint len)
{
if (len < MAX_ND_SIZE) {
EthernetII pdu(data, len);
return _parse_nd(pdu);
}
return false;
}
bool nd_reply(uint8_t *reply_data, uint8_t *request_data, uint len,
const uint8_t *mac)
{
EthernetII pdu(request_data, len);
if (_parse_nd(pdu)) {
HWAddress<ETHER_ADDR_LEN> source_mac(mac);
const ICMPv6& icmp6 = pdu.rfind_pdu<ICMPv6>();
EthernetII reply(pdu.src_addr(), pdu.dst_addr());
ICMPv6 nd_reply(ICMPv6::NEIGHBOUR_ADVERT);
nd_reply.target_link_layer_addr(source_mac);
nd_reply.target_addr(icmp6.target_addr());
reply /= nd_reply;
_serialize_reply(reply_data, reply);
return true;
}
return false;
}