blob: fa8319cc5ae1c3ade52fc6049029c42539ca1af7 [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) {
Patrick Williams099fb092023-05-10 07:50:31 -050042 if (!ec)
43 {
44 boost::asio::post(*io, [this]() { startRmcpReceive(); });
45 handleRmcpPacket();
46 }
47 });
Tom Joseph7fd26dd2017-03-14 15:26:26 +053048}
49
Alvin Wang8c0bf982019-10-21 10:30:07 +080050int EventLoop::getVLANID(const std::string channel)
Vernon Maueryd92bc322019-03-15 15:24:30 -070051{
Alvin Wang8c0bf982019-10-21 10:30:07 +080052 int vlanid = 0;
53 if (channel.empty())
Vernon Maueryd92bc322019-03-15 15:24:30 -070054 {
Alvin Wang8c0bf982019-10-21 10:30:07 +080055 return 0;
56 }
57
Patrick Williams0a590622022-07-22 19:26:53 -050058 sdbusplus::bus_t bus{ipmid_get_sd_bus_connection()};
Alvin Wang8c0bf982019-10-21 10:30:07 +080059 // Enumerate all VLAN + ETHERNET interfaces
60 auto req = bus.new_method_call(MAPPER_BUS_NAME, MAPPER_OBJ, MAPPER_INTF,
61 "GetSubTree");
62 req.append(PATH_ROOT, 0,
63 std::vector<std::string>{INTF_VLAN, INTF_ETHERNET});
64 ObjectTree objs;
65 try
66 {
67 auto reply = bus.call(req);
68 reply.read(objs);
69 }
Patrick Williams12d199b2021-10-06 12:36:48 -050070 catch (const std::exception& e)
Alvin Wang8c0bf982019-10-21 10:30:07 +080071 {
George Liu7b7f25f2022-07-04 17:07:32 +080072 lg2::error("getVLANID: failed to execute/read GetSubTree: {ERROR}",
73 "ERROR", e);
Alvin Wang8c0bf982019-10-21 10:30:07 +080074 return 0;
75 }
76
77 std::string ifService, logicalPath;
78 for (const auto& [path, impls] : objs)
79 {
80 if (path.find(channel) == path.npos)
81 {
82 continue;
83 }
84 for (const auto& [service, intfs] : impls)
85 {
86 bool vlan = false;
87 bool ethernet = false;
88 for (const auto& intf : intfs)
89 {
90 if (intf == INTF_VLAN)
91 {
92 vlan = true;
93 }
94 else if (intf == INTF_ETHERNET)
95 {
96 ethernet = true;
97 }
98 }
99 if (ifService.empty() && (vlan || ethernet))
100 {
101 ifService = service;
102 }
103 if (logicalPath.empty() && vlan)
104 {
105 logicalPath = path;
106 }
107 }
108 }
109
110 // VLAN devices will always have a separate logical object
111 if (logicalPath.empty())
112 {
113 return 0;
114 }
115
116 Value value;
117 auto method = bus.new_method_call(ifService.c_str(), logicalPath.c_str(),
118 PROP_INTF, METHOD_GET);
119 method.append(INTF_VLAN, "Id");
120 try
121 {
122 auto method_reply = bus.call(method);
123 method_reply.read(value);
124 }
Patrick Williams12d199b2021-10-06 12:36:48 -0500125 catch (const std::exception& e)
Alvin Wang8c0bf982019-10-21 10:30:07 +0800126 {
George Liu7b7f25f2022-07-04 17:07:32 +0800127 lg2::error("getVLANID: failed to execute/read VLAN Id: {ERROR}",
128 "ERROR", e);
Alvin Wang8c0bf982019-10-21 10:30:07 +0800129 return 0;
130 }
131
132 vlanid = std::get<uint32_t>(value);
133 if ((vlanid & VLAN_VALUE_MASK) != vlanid)
134 {
George Liu7b7f25f2022-07-04 17:07:32 +0800135 lg2::error("networkd returned an invalid vlan: {VLAN}", "VLAN", vlanid);
Alvin Wang8c0bf982019-10-21 10:30:07 +0800136 return 0;
137 }
138
139 return vlanid;
140}
141
142int EventLoop::setupSocket(std::shared_ptr<sdbusplus::asio::connection>& bus,
143 std::string channel, uint16_t reqPort)
144{
145 std::string iface = channel;
146 static constexpr const char* unboundIface = "rmcpp";
147 if (channel == "")
148 {
149 iface = channel = unboundIface;
150 }
151 else
152 {
153 // If VLANID of this channel is set, bind the socket to this
154 // VLAN logic device
155 auto vlanid = getVLANID(channel);
156 if (vlanid)
157 {
158 iface = iface + "." + std::to_string(vlanid);
George Liu7b7f25f2022-07-04 17:07:32 +0800159 lg2::debug("This channel has VLAN id: {VLAN}", "VLAN", vlanid);
Alvin Wang8c0bf982019-10-21 10:30:07 +0800160 }
Vernon Maueryd92bc322019-03-15 15:24:30 -0700161 }
162 // Create our own socket if SysD did not supply one.
163 int listensFdCount = sd_listen_fds(0);
164 if (listensFdCount > 1)
165 {
George Liu7b7f25f2022-07-04 17:07:32 +0800166 lg2::error("Too many file descriptors received, listensFdCount: {FD}",
167 "FD", listensFdCount);
Vernon Maueryd92bc322019-03-15 15:24:30 -0700168 return EXIT_FAILURE;
169 }
170 if (listensFdCount == 1)
171 {
172 int openFd = SD_LISTEN_FDS_START;
173 if (!sd_is_socket(openFd, AF_UNSPEC, SOCK_DGRAM, -1))
174 {
George Liu7b7f25f2022-07-04 17:07:32 +0800175 lg2::error("Failed to set up systemd-passed socket: {ERROR}",
176 "ERROR", strerror(errno));
Vernon Maueryd92bc322019-03-15 15:24:30 -0700177 return EXIT_FAILURE;
178 }
179 udpSocket = std::make_shared<boost::asio::ip::udp::socket>(
180 *io, boost::asio::ip::udp::v6(), openFd);
181 }
182 else
183 {
184 // asio does not natively offer a way to bind to an interface
185 // so it must be done in steps
186 boost::asio::ip::udp::endpoint ep(boost::asio::ip::udp::v6(), reqPort);
187 udpSocket = std::make_shared<boost::asio::ip::udp::socket>(*io);
188 udpSocket->open(ep.protocol());
189 // bind
190 udpSocket->set_option(
191 boost::asio::ip::udp::socket::reuse_address(true));
192 udpSocket->bind(ep);
193 }
194 // SO_BINDTODEVICE
195 char nameout[IFNAMSIZ];
196 unsigned int lenout = sizeof(nameout);
197 if ((::getsockopt(udpSocket->native_handle(), SOL_SOCKET, SO_BINDTODEVICE,
198 nameout, &lenout) == -1))
199 {
George Liu7b7f25f2022-07-04 17:07:32 +0800200 lg2::error("Failed to read bound device: {ERROR}", "ERROR",
201 strerror(errno));
Vernon Maueryd92bc322019-03-15 15:24:30 -0700202 }
203 if (iface != nameout && iface != unboundIface)
204 {
205 // SO_BINDTODEVICE
206 if ((::setsockopt(udpSocket->native_handle(), SOL_SOCKET,
207 SO_BINDTODEVICE, iface.c_str(),
208 iface.size() + 1) == -1))
209 {
George Liu7b7f25f2022-07-04 17:07:32 +0800210 lg2::error("Failed to bind to requested interface: {ERROR}",
211 "ERROR", strerror(errno));
Vernon Maueryd92bc322019-03-15 15:24:30 -0700212 return EXIT_FAILURE;
213 }
George Liu7b7f25f2022-07-04 17:07:32 +0800214 lg2::info("Bind to interface: {INTERFACE}", "INTERFACE", iface);
Vernon Maueryd92bc322019-03-15 15:24:30 -0700215 }
216 // cannot be constexpr because it gets passed by address
217 const int option_enabled = 1;
218 // common socket stuff; set options to get packet info (DST addr)
219 ::setsockopt(udpSocket->native_handle(), IPPROTO_IP, IP_PKTINFO,
220 &option_enabled, sizeof(option_enabled));
221 ::setsockopt(udpSocket->native_handle(), IPPROTO_IPV6, IPV6_RECVPKTINFO,
222 &option_enabled, sizeof(option_enabled));
223
224 // set the dbus name
Alvin Wang8c0bf982019-10-21 10:30:07 +0800225 std::string busName = "xyz.openbmc_project.Ipmi.Channel." + channel;
Vernon Maueryd92bc322019-03-15 15:24:30 -0700226 try
227 {
228 bus->request_name(busName.c_str());
229 }
230 catch (const std::exception& e)
231 {
George Liu7b7f25f2022-07-04 17:07:32 +0800232 lg2::error("Failed to acquire D-Bus name: {NAME}: {ERROR}", "NAME",
233 busName, "ERROR", e);
Vernon Maueryd92bc322019-03-15 15:24:30 -0700234 return EXIT_FAILURE;
235 }
236 return 0;
237}
238
Vernon Mauerycbccb052018-10-24 13:52:22 -0700239int EventLoop::startEventLoop()
Tom Joseph7fd26dd2017-03-14 15:26:26 +0530240{
Vernon Mauery22c8a212018-10-24 14:51:23 -0700241 // set up boost::asio signal handling
242 boost::asio::signal_set signals(*io, SIGINT, SIGTERM);
George Liube1470c2022-07-04 14:33:24 +0800243 signals.async_wait([this](const boost::system::error_code& /* error */,
244 int /* signalNumber */) {
245 udpSocket->cancel();
246 udpSocket->close();
247 io->stop();
248 });
Tom Joseph7fd26dd2017-03-14 15:26:26 +0530249
Vernon Mauery7a0142c2018-11-09 08:38:16 -0800250 startRmcpReceive();
Tom Joseph7fd26dd2017-03-14 15:26:26 +0530251
Vernon Mauerycbccb052018-10-24 13:52:22 -0700252 io->run();
Tom Joseph7fd26dd2017-03-14 15:26:26 +0530253
Vernon Mauerycbccb052018-10-24 13:52:22 -0700254 return EXIT_SUCCESS;
Tom Joseph7fd26dd2017-03-14 15:26:26 +0530255}
256
Tom Joseph7fd26dd2017-03-14 15:26:26 +0530257} // namespace eventloop