114fe6698SNan Zhou // Copyright 2021 Google LLC
214fe6698SNan Zhou //
314fe6698SNan Zhou // Licensed under the Apache License, Version 2.0 (the "License");
414fe6698SNan Zhou // you may not use this file except in compliance with the License.
514fe6698SNan Zhou // You may obtain a copy of the License at
614fe6698SNan Zhou //
714fe6698SNan Zhou //      http://www.apache.org/licenses/LICENSE-2.0
814fe6698SNan Zhou //
914fe6698SNan Zhou // Unless required by applicable law or agreed to in writing, software
1014fe6698SNan Zhou // distributed under the License is distributed on an "AS IS" BASIS,
1114fe6698SNan Zhou // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1214fe6698SNan Zhou // See the License for the specific language governing permissions and
1314fe6698SNan Zhou // limitations under the License.
1414fe6698SNan Zhou 
1514fe6698SNan Zhou #include "nemora.hpp"
1614fe6698SNan Zhou 
1714fe6698SNan Zhou #include "default_addresses.h"
1814fe6698SNan Zhou 
1914fe6698SNan Zhou #include <netinet/in.h>
2014fe6698SNan Zhou #include <time.h>
2114fe6698SNan Zhou #include <unistd.h>
2214fe6698SNan Zhou 
2314fe6698SNan Zhou #include <phosphor-logging/log.hpp>
2414fe6698SNan Zhou #include <sdbusplus/bus.hpp>
2514fe6698SNan Zhou #include <sdbusplus/exception.hpp>
2614fe6698SNan Zhou 
2714fe6698SNan Zhou #include <cstdint>
2814fe6698SNan Zhou #include <cstring>
2914fe6698SNan Zhou #include <variant>
3014fe6698SNan Zhou 
3114fe6698SNan Zhou using phosphor::logging::level;
3214fe6698SNan Zhou using phosphor::logging::log;
3314fe6698SNan Zhou using sdbusplus::exception::SdBusError;
3414fe6698SNan Zhou 
3514fe6698SNan Zhou constexpr auto MAC_FORMAT = "%02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx";
3614fe6698SNan Zhou constexpr auto MAC_INTERFACE = "xyz.openbmc_project.Network.MACAddress";
3714fe6698SNan Zhou constexpr auto NETWORK_INTERFACE = "xyz.openbmc_project.Network";
3814fe6698SNan Zhou constexpr auto PROP_INTERFACE = "org.freedesktop.DBus.Properties";
3914fe6698SNan Zhou constexpr auto IFACE_ROOT = "/xyz/openbmc_project/network/";
4014fe6698SNan Zhou 
ParseMac(const std::string & mac_addr,MacAddr * mac)4114fe6698SNan Zhou bool Nemora::ParseMac(const std::string& mac_addr, MacAddr* mac)
4214fe6698SNan Zhou {
43*2be45238SPatrick Williams     int ret = sscanf(mac_addr.c_str(), MAC_FORMAT, mac->octet, mac->octet + 1,
44*2be45238SPatrick Williams                      mac->octet + 2, mac->octet + 3, mac->octet + 4,
45*2be45238SPatrick Williams                      mac->octet + 5);
4614fe6698SNan Zhou     return (ret == MAC_ADDR_SIZE);
4714fe6698SNan Zhou }
4814fe6698SNan Zhou 
GetMacAddr(MacAddr * mac,const std::string & iface_path)4914fe6698SNan Zhou bool Nemora::GetMacAddr(MacAddr* mac, const std::string& iface_path)
5014fe6698SNan Zhou {
5114fe6698SNan Zhou     if (mac == nullptr)
5214fe6698SNan Zhou     {
5314fe6698SNan Zhou         log<level::ERR>("Nemora::GetMacAddr MAC Address is nullptr");
5414fe6698SNan Zhou         return false;
5514fe6698SNan Zhou     }
5614fe6698SNan Zhou     auto dbus = sdbusplus::bus::new_default();
5759ac2c2dSPatrick Williams     sdbusplus::message_t reply;
5814fe6698SNan Zhou 
5914fe6698SNan Zhou     try
6014fe6698SNan Zhou     {
6114fe6698SNan Zhou         auto networkd_call = dbus.new_method_call(
6214fe6698SNan Zhou             NETWORK_INTERFACE, iface_path.c_str(), PROP_INTERFACE, "Get");
6314fe6698SNan Zhou         networkd_call.append(MAC_INTERFACE, "MACAddress");
6414fe6698SNan Zhou 
6514fe6698SNan Zhou         reply = dbus.call(networkd_call);
6614fe6698SNan Zhou     }
6714fe6698SNan Zhou     catch (const SdBusError& e)
6814fe6698SNan Zhou     {
6914fe6698SNan Zhou         log<level::ERR>(
7014fe6698SNan Zhou             "Nemora::GetMacAddr failed to call Network D-Bus interface");
7114fe6698SNan Zhou         return false;
7214fe6698SNan Zhou     }
7314fe6698SNan Zhou 
7414fe6698SNan Zhou     std::variant<std::string> result;
7514fe6698SNan Zhou     reply.read(result);
7614fe6698SNan Zhou     auto mac_addr = std::get<std::string>(result);
7714fe6698SNan Zhou     if (!ParseMac(mac_addr, mac))
7814fe6698SNan Zhou     {
7914fe6698SNan Zhou         log<level::ERR>("Nemora::GetMacAddr Failed to parse MAC Address");
8014fe6698SNan Zhou         return false;
8114fe6698SNan Zhou     }
8214fe6698SNan Zhou     return true;
8314fe6698SNan Zhou }
8414fe6698SNan Zhou 
InitEventData()8514fe6698SNan Zhou void Nemora::InitEventData()
8614fe6698SNan Zhou {
8714fe6698SNan Zhou     event_data_.type = NemoraDatagramType::NemoraEvent;
8814fe6698SNan Zhou 
8914fe6698SNan Zhou     // UDP IPv4 addr for POST
9014fe6698SNan Zhou     event_data_.destination.sin_family = AF_INET;
9114fe6698SNan Zhou     event_data_.destination.sin_port = htons(DEFAULT_ADDRESSES_TARGET_PORT);
9214fe6698SNan Zhou 
9314fe6698SNan Zhou     // UDP IPv6 addr for POST
9414fe6698SNan Zhou     event_data_.destination6.sin6_family = AF_INET6;
9514fe6698SNan Zhou     event_data_.destination6.sin6_port = htons(DEFAULT_ADDRESSES_TARGET_PORT);
9614fe6698SNan Zhou }
9714fe6698SNan Zhou 
Nemora()9814fe6698SNan Zhou Nemora::Nemora()
9914fe6698SNan Zhou {
10014fe6698SNan Zhou     InitEventData();
10114fe6698SNan Zhou }
10214fe6698SNan Zhou 
Nemora(const std::string & iface_name,const in_addr ipv4,const in6_addr ipv6)10314fe6698SNan Zhou Nemora::Nemora(const std::string& iface_name, const in_addr ipv4,
10414fe6698SNan Zhou                const in6_addr ipv6) :
10514fe6698SNan Zhou     socketManager_(),
10614fe6698SNan Zhou     hostManager_(), iface_path_{std::string(IFACE_ROOT) + iface_name}
10714fe6698SNan Zhou {
10814fe6698SNan Zhou     InitEventData();
10914fe6698SNan Zhou     event_data_.destination.sin_addr = ipv4;
11014fe6698SNan Zhou     event_data_.destination6.sin6_addr = ipv6;
11114fe6698SNan Zhou }
11214fe6698SNan Zhou 
UdpPoll()11314fe6698SNan Zhou void Nemora::UdpPoll()
11414fe6698SNan Zhou {
11514fe6698SNan Zhou     auto postcodes = hostManager_.DrainPostcodes();
11614fe6698SNan Zhou 
11714fe6698SNan Zhou     // Don't bother updating if there is no POST code
11814fe6698SNan Zhou     // EC supports a flag EC_NEMORA_UDP_CONFIG_MASK_PERIODIC to send
11914fe6698SNan Zhou     // periodic updates, which is non-POR for gBMC for now.
12014fe6698SNan Zhou     bool shouldBroadcast = !postcodes.empty();
12114fe6698SNan Zhou 
12214fe6698SNan Zhou     UpdateEventData(std::move(postcodes));
12314fe6698SNan Zhou 
12414fe6698SNan Zhou     log<level::INFO>("UpdateEventData gets called.");
12514fe6698SNan Zhou 
12614fe6698SNan Zhou     if (shouldBroadcast)
12714fe6698SNan Zhou     {
12814fe6698SNan Zhou         log<level::INFO>("Should broadcast");
12914fe6698SNan Zhou         std::lock_guard<std::mutex> lock(event_data_mutex_);
13014fe6698SNan Zhou         socketManager_.SendDatagram(static_cast<NemoraDatagram*>(&event_data_));
13114fe6698SNan Zhou     }
13214fe6698SNan Zhou 
13314fe6698SNan Zhou     sleep(20);
13414fe6698SNan Zhou }
13514fe6698SNan Zhou 
UpdateEventData(std::vector<uint64_t> && postcodes)13614fe6698SNan Zhou void Nemora::UpdateEventData(std::vector<uint64_t>&& postcodes)
13714fe6698SNan Zhou {
13814fe6698SNan Zhou     MacAddr mac;
13914fe6698SNan Zhou     GetMacAddr(&mac, iface_path_);
14014fe6698SNan Zhou 
14114fe6698SNan Zhou     std::lock_guard<std::mutex> lock(event_data_mutex_);
14214fe6698SNan Zhou 
14314fe6698SNan Zhou     memcpy(event_data_.mac, mac.octet, sizeof(MacAddr));
14414fe6698SNan Zhou 
14514fe6698SNan Zhou     event_data_.postcodes = std::move(postcodes);
14614fe6698SNan Zhou     event_data_.sent_time_s = time(0);
14714fe6698SNan Zhou }
148