blob: df985c45e5187a8ffd933648e5a159dac360bb90 [file] [log] [blame]
Vernon Mauery9e801a22018-10-12 13:20:49 -07001#include "sd_event_loop.hpp"
2
3#include "main.hpp"
4#include "message_handler.hpp"
5
George Liu7b7f25f2022-07-04 17:07:32 +08006#include <error.h>
Dave Cobbley2c15f0c2017-11-13 16:19:09 -08007#include <netinet/in.h>
Tom Joseph7fd26dd2017-03-14 15:26:26 +05308#include <sys/ioctl.h>
Dave Cobbley2c15f0c2017-11-13 16:19:09 -08009#include <sys/socket.h>
Tom Joseph7fd26dd2017-03-14 15:26:26 +053010#include <systemd/sd-daemon.h>
Vernon Mauery9e801a22018-10-12 13:20:49 -070011
Vernon Mauerycbccb052018-10-24 13:52:22 -070012#include <boost/asio/io_context.hpp>
Ed Tanous07bb0952020-08-17 23:32:38 -070013#include <boost/asio/signal_set.hpp>
George Liu7b7f25f2022-07-04 17:07:32 +080014#include <phosphor-logging/lg2.hpp>
Vernon Mauerycbccb052018-10-24 13:52:22 -070015#include <sdbusplus/asio/sd_event.hpp>
Alvin Wang8c0bf982019-10-21 10:30:07 +080016#include <user_channel/channel_layer.hpp>
Tom Joseph7fd26dd2017-03-14 15:26:26 +053017
18namespace eventloop
19{
Tom Joseph7fd26dd2017-03-14 15:26:26 +053020
Vernon Mauery7a0142c2018-11-09 08:38:16 -080021void EventLoop::handleRmcpPacket()
Tom Joseph7fd26dd2017-03-14 15:26:26 +053022{
Tom Joseph7fd26dd2017-03-14 15:26:26 +053023 try
24 {
Vernon Mauery7a0142c2018-11-09 08:38:16 -080025 auto channelPtr = std::make_shared<udpsocket::Channel>(udpSocket);
Tom Joseph7fd26dd2017-03-14 15:26:26 +053026
27 // Initialize the Message Handler with the socket channel
Vernon Mauery8d6f2002018-11-07 09:55:53 -080028 auto msgHandler = std::make_shared<message::Handler>(channelPtr, io);
Tom Joseph7fd26dd2017-03-14 15:26:26 +053029
Vernon Mauery8d6f2002018-11-07 09:55:53 -080030 msgHandler->processIncoming();
Tom Joseph7fd26dd2017-03-14 15:26:26 +053031 }
Vernon Mauery7a0142c2018-11-09 08:38:16 -080032 catch (const std::exception& e)
Tom Joseph7fd26dd2017-03-14 15:26:26 +053033 {
George Liu7b7f25f2022-07-04 17:07:32 +080034 lg2::error("Executing the IPMI message failed: {ERROR}", "ERROR", e);
Tom Joseph7fd26dd2017-03-14 15:26:26 +053035 }
Vernon Mauery7a0142c2018-11-09 08:38:16 -080036}
Tom Joseph7fd26dd2017-03-14 15:26:26 +053037
Vernon Mauery7a0142c2018-11-09 08:38:16 -080038void EventLoop::startRmcpReceive()
39{
40 udpSocket->async_wait(boost::asio::socket_base::wait_read,
41 [this](const boost::system::error_code& ec) {
42 if (!ec)
43 {
Ed Tanous1b32b8e2023-03-06 13:59:18 -080044 boost::asio::post(
45 *io, [this]() { startRmcpReceive(); });
Vernon Mauery7a0142c2018-11-09 08:38:16 -080046 handleRmcpPacket();
47 }
48 });
Tom Joseph7fd26dd2017-03-14 15:26:26 +053049}
50
Alvin Wang8c0bf982019-10-21 10:30:07 +080051int EventLoop::getVLANID(const std::string channel)
Vernon Maueryd92bc322019-03-15 15:24:30 -070052{
Alvin Wang8c0bf982019-10-21 10:30:07 +080053 int vlanid = 0;
54 if (channel.empty())
Vernon Maueryd92bc322019-03-15 15:24:30 -070055 {
Alvin Wang8c0bf982019-10-21 10:30:07 +080056 return 0;
57 }
58
Patrick Williams0a590622022-07-22 19:26:53 -050059 sdbusplus::bus_t bus{ipmid_get_sd_bus_connection()};
Alvin Wang8c0bf982019-10-21 10:30:07 +080060 // Enumerate all VLAN + ETHERNET interfaces
61 auto req = bus.new_method_call(MAPPER_BUS_NAME, MAPPER_OBJ, MAPPER_INTF,
62 "GetSubTree");
63 req.append(PATH_ROOT, 0,
64 std::vector<std::string>{INTF_VLAN, INTF_ETHERNET});
65 ObjectTree objs;
66 try
67 {
68 auto reply = bus.call(req);
69 reply.read(objs);
70 }
Patrick Williams12d199b2021-10-06 12:36:48 -050071 catch (const std::exception& e)
Alvin Wang8c0bf982019-10-21 10:30:07 +080072 {
George Liu7b7f25f2022-07-04 17:07:32 +080073 lg2::error("getVLANID: failed to execute/read GetSubTree: {ERROR}",
74 "ERROR", e);
Alvin Wang8c0bf982019-10-21 10:30:07 +080075 return 0;
76 }
77
78 std::string ifService, logicalPath;
79 for (const auto& [path, impls] : objs)
80 {
81 if (path.find(channel) == path.npos)
82 {
83 continue;
84 }
85 for (const auto& [service, intfs] : impls)
86 {
87 bool vlan = false;
88 bool ethernet = false;
89 for (const auto& intf : intfs)
90 {
91 if (intf == INTF_VLAN)
92 {
93 vlan = true;
94 }
95 else if (intf == INTF_ETHERNET)
96 {
97 ethernet = true;
98 }
99 }
100 if (ifService.empty() && (vlan || ethernet))
101 {
102 ifService = service;
103 }
104 if (logicalPath.empty() && vlan)
105 {
106 logicalPath = path;
107 }
108 }
109 }
110
111 // VLAN devices will always have a separate logical object
112 if (logicalPath.empty())
113 {
114 return 0;
115 }
116
117 Value value;
118 auto method = bus.new_method_call(ifService.c_str(), logicalPath.c_str(),
119 PROP_INTF, METHOD_GET);
120 method.append(INTF_VLAN, "Id");
121 try
122 {
123 auto method_reply = bus.call(method);
124 method_reply.read(value);
125 }
Patrick Williams12d199b2021-10-06 12:36:48 -0500126 catch (const std::exception& e)
Alvin Wang8c0bf982019-10-21 10:30:07 +0800127 {
George Liu7b7f25f2022-07-04 17:07:32 +0800128 lg2::error("getVLANID: failed to execute/read VLAN Id: {ERROR}",
129 "ERROR", e);
Alvin Wang8c0bf982019-10-21 10:30:07 +0800130 return 0;
131 }
132
133 vlanid = std::get<uint32_t>(value);
134 if ((vlanid & VLAN_VALUE_MASK) != vlanid)
135 {
George Liu7b7f25f2022-07-04 17:07:32 +0800136 lg2::error("networkd returned an invalid vlan: {VLAN}", "VLAN", vlanid);
Alvin Wang8c0bf982019-10-21 10:30:07 +0800137 return 0;
138 }
139
140 return vlanid;
141}
142
143int EventLoop::setupSocket(std::shared_ptr<sdbusplus::asio::connection>& bus,
144 std::string channel, uint16_t reqPort)
145{
146 std::string iface = channel;
147 static constexpr const char* unboundIface = "rmcpp";
148 if (channel == "")
149 {
150 iface = channel = unboundIface;
151 }
152 else
153 {
154 // If VLANID of this channel is set, bind the socket to this
155 // VLAN logic device
156 auto vlanid = getVLANID(channel);
157 if (vlanid)
158 {
159 iface = iface + "." + std::to_string(vlanid);
George Liu7b7f25f2022-07-04 17:07:32 +0800160 lg2::debug("This channel has VLAN id: {VLAN}", "VLAN", vlanid);
Alvin Wang8c0bf982019-10-21 10:30:07 +0800161 }
Vernon Maueryd92bc322019-03-15 15:24:30 -0700162 }
163 // Create our own socket if SysD did not supply one.
164 int listensFdCount = sd_listen_fds(0);
165 if (listensFdCount > 1)
166 {
George Liu7b7f25f2022-07-04 17:07:32 +0800167 lg2::error("Too many file descriptors received, listensFdCount: {FD}",
168 "FD", listensFdCount);
Vernon Maueryd92bc322019-03-15 15:24:30 -0700169 return EXIT_FAILURE;
170 }
171 if (listensFdCount == 1)
172 {
173 int openFd = SD_LISTEN_FDS_START;
174 if (!sd_is_socket(openFd, AF_UNSPEC, SOCK_DGRAM, -1))
175 {
George Liu7b7f25f2022-07-04 17:07:32 +0800176 lg2::error("Failed to set up systemd-passed socket: {ERROR}",
177 "ERROR", strerror(errno));
Vernon Maueryd92bc322019-03-15 15:24:30 -0700178 return EXIT_FAILURE;
179 }
180 udpSocket = std::make_shared<boost::asio::ip::udp::socket>(
181 *io, boost::asio::ip::udp::v6(), openFd);
182 }
183 else
184 {
185 // asio does not natively offer a way to bind to an interface
186 // so it must be done in steps
187 boost::asio::ip::udp::endpoint ep(boost::asio::ip::udp::v6(), reqPort);
188 udpSocket = std::make_shared<boost::asio::ip::udp::socket>(*io);
189 udpSocket->open(ep.protocol());
190 // bind
191 udpSocket->set_option(
192 boost::asio::ip::udp::socket::reuse_address(true));
193 udpSocket->bind(ep);
194 }
195 // SO_BINDTODEVICE
196 char nameout[IFNAMSIZ];
197 unsigned int lenout = sizeof(nameout);
198 if ((::getsockopt(udpSocket->native_handle(), SOL_SOCKET, SO_BINDTODEVICE,
199 nameout, &lenout) == -1))
200 {
George Liu7b7f25f2022-07-04 17:07:32 +0800201 lg2::error("Failed to read bound device: {ERROR}", "ERROR",
202 strerror(errno));
Vernon Maueryd92bc322019-03-15 15:24:30 -0700203 }
204 if (iface != nameout && iface != unboundIface)
205 {
206 // SO_BINDTODEVICE
207 if ((::setsockopt(udpSocket->native_handle(), SOL_SOCKET,
208 SO_BINDTODEVICE, iface.c_str(),
209 iface.size() + 1) == -1))
210 {
George Liu7b7f25f2022-07-04 17:07:32 +0800211 lg2::error("Failed to bind to requested interface: {ERROR}",
212 "ERROR", strerror(errno));
Vernon Maueryd92bc322019-03-15 15:24:30 -0700213 return EXIT_FAILURE;
214 }
George Liu7b7f25f2022-07-04 17:07:32 +0800215 lg2::info("Bind to interface: {INTERFACE}", "INTERFACE", iface);
Vernon Maueryd92bc322019-03-15 15:24:30 -0700216 }
217 // cannot be constexpr because it gets passed by address
218 const int option_enabled = 1;
219 // common socket stuff; set options to get packet info (DST addr)
220 ::setsockopt(udpSocket->native_handle(), IPPROTO_IP, IP_PKTINFO,
221 &option_enabled, sizeof(option_enabled));
222 ::setsockopt(udpSocket->native_handle(), IPPROTO_IPV6, IPV6_RECVPKTINFO,
223 &option_enabled, sizeof(option_enabled));
224
225 // set the dbus name
Alvin Wang8c0bf982019-10-21 10:30:07 +0800226 std::string busName = "xyz.openbmc_project.Ipmi.Channel." + channel;
Vernon Maueryd92bc322019-03-15 15:24:30 -0700227 try
228 {
229 bus->request_name(busName.c_str());
230 }
231 catch (const std::exception& e)
232 {
George Liu7b7f25f2022-07-04 17:07:32 +0800233 lg2::error("Failed to acquire D-Bus name: {NAME}: {ERROR}", "NAME",
234 busName, "ERROR", e);
Vernon Maueryd92bc322019-03-15 15:24:30 -0700235 return EXIT_FAILURE;
236 }
237 return 0;
238}
239
Vernon Mauerycbccb052018-10-24 13:52:22 -0700240int EventLoop::startEventLoop()
Tom Joseph7fd26dd2017-03-14 15:26:26 +0530241{
Vernon Mauery22c8a212018-10-24 14:51:23 -0700242 // set up boost::asio signal handling
243 boost::asio::signal_set signals(*io, SIGINT, SIGTERM);
George Liube1470c2022-07-04 14:33:24 +0800244 signals.async_wait([this](const boost::system::error_code& /* error */,
245 int /* signalNumber */) {
246 udpSocket->cancel();
247 udpSocket->close();
248 io->stop();
249 });
Tom Joseph7fd26dd2017-03-14 15:26:26 +0530250
Vernon Mauery7a0142c2018-11-09 08:38:16 -0800251 startRmcpReceive();
Tom Joseph7fd26dd2017-03-14 15:26:26 +0530252
Vernon Mauerycbccb052018-10-24 13:52:22 -0700253 io->run();
Tom Joseph7fd26dd2017-03-14 15:26:26 +0530254
Vernon Mauerycbccb052018-10-24 13:52:22 -0700255 return EXIT_SUCCESS;
Tom Joseph7fd26dd2017-03-14 15:26:26 +0530256}
257
Tom Joseph7fd26dd2017-03-14 15:26:26 +0530258} // namespace eventloop