1 // Copyright 2021 Google LLC
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 //      http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 
15 #include "nemora.hpp"
16 
17 #include "default_addresses.h"
18 
19 #include <netinet/in.h>
20 #include <time.h>
21 #include <unistd.h>
22 
23 #include <phosphor-logging/log.hpp>
24 #include <sdbusplus/bus.hpp>
25 #include <sdbusplus/exception.hpp>
26 
27 #include <cstdint>
28 #include <cstring>
29 #include <functional>
30 #include <iostream>
31 #include <variant>
32 
33 using phosphor::logging::level;
34 using phosphor::logging::log;
35 using sdbusplus::exception::SdBusError;
36 
37 constexpr auto MAC_FORMAT = "%02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx";
38 constexpr auto MAC_INTERFACE = "xyz.openbmc_project.Network.MACAddress";
39 constexpr auto NETWORK_INTERFACE = "xyz.openbmc_project.Network";
40 constexpr auto PROP_INTERFACE = "org.freedesktop.DBus.Properties";
41 constexpr auto IFACE_ROOT = "/xyz/openbmc_project/network/";
42 
43 bool Nemora::ParseMac(const std::string& mac_addr, MacAddr* mac)
44 {
45     int ret = sscanf(mac_addr.c_str(), MAC_FORMAT, mac->octet, mac->octet + 1,
46                      mac->octet + 2, mac->octet + 3, mac->octet + 4,
47                      mac->octet + 5);
48     return (ret == MAC_ADDR_SIZE);
49 }
50 
51 bool Nemora::GetMacAddr(MacAddr* mac, const std::string& iface_path)
52 {
53     if (mac == nullptr)
54     {
55         log<level::ERR>("Nemora::GetMacAddr MAC Address is nullptr");
56         return false;
57     }
58     auto dbus = sdbusplus::bus::new_default();
59     sdbusplus::message_t reply;
60 
61     try
62     {
63         auto networkd_call = dbus.new_method_call(
64             NETWORK_INTERFACE, iface_path.c_str(), PROP_INTERFACE, "Get");
65         networkd_call.append(MAC_INTERFACE, "MACAddress");
66 
67         reply = dbus.call(networkd_call);
68     }
69     catch (const SdBusError& e)
70     {
71         log<level::ERR>(
72             "Nemora::GetMacAddr failed to call Network D-Bus interface");
73         return false;
74     }
75 
76     std::variant<std::string> result;
77     reply.read(result);
78     auto mac_addr = std::get<std::string>(result);
79     if (!ParseMac(mac_addr, mac))
80     {
81         log<level::ERR>("Nemora::GetMacAddr Failed to parse MAC Address");
82         return false;
83     }
84     return true;
85 }
86 
87 void Nemora::InitEventData()
88 {
89     event_data_.type = NemoraDatagramType::NemoraEvent;
90 
91     // UDP IPv4 addr for POST
92     event_data_.destination.sin_family = AF_INET;
93     event_data_.destination.sin_port = htons(DEFAULT_ADDRESSES_TARGET_PORT);
94 
95     // UDP IPv6 addr for POST
96     event_data_.destination6.sin6_family = AF_INET6;
97     event_data_.destination6.sin6_port = htons(DEFAULT_ADDRESSES_TARGET_PORT);
98 }
99 
100 Nemora::Nemora()
101 {
102     InitEventData();
103 }
104 
105 Nemora::Nemora(const std::string& iface_name, const in_addr ipv4,
106                const in6_addr ipv6) :
107     socketManager_(),
108     hostManager_(), iface_path_{std::string(IFACE_ROOT) + iface_name}
109 {
110     InitEventData();
111     event_data_.destination.sin_addr = ipv4;
112     event_data_.destination6.sin6_addr = ipv6;
113 }
114 
115 void Nemora::UdpPoll()
116 {
117     auto postcodes = hostManager_.DrainPostcodes();
118 
119     // Don't bother updating if there is no POST code
120     // EC supports a flag EC_NEMORA_UDP_CONFIG_MASK_PERIODIC to send
121     // periodic updates, which is non-POR for gBMC for now.
122     bool shouldBroadcast = !postcodes.empty();
123 
124     UpdateEventData(std::move(postcodes));
125 
126     log<level::INFO>("UpdateEventData gets called.");
127 
128     if (shouldBroadcast)
129     {
130         log<level::INFO>("Should broadcast");
131         std::lock_guard<std::mutex> lock(event_data_mutex_);
132         socketManager_.SendDatagram(static_cast<NemoraDatagram*>(&event_data_));
133     }
134 
135     sleep(20);
136 }
137 
138 void Nemora::UpdateEventData(std::vector<uint64_t>&& postcodes)
139 {
140     MacAddr mac;
141     GetMacAddr(&mac, iface_path_);
142 
143     std::lock_guard<std::mutex> lock(event_data_mutex_);
144 
145     memcpy(event_data_.mac, mac.octet, sizeof(MacAddr));
146 
147     event_data_.postcodes = std::move(postcodes);
148     event_data_.sent_time_s = time(0);
149 }
150