blob: 0022769194d53b69d2cb5ec46596c1048679f784 [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
Dave Cobbley2c15f0c2017-11-13 16:19:09 -08006#include <netinet/in.h>
Tom Joseph7fd26dd2017-03-14 15:26:26 +05307#include <sys/ioctl.h>
Dave Cobbley2c15f0c2017-11-13 16:19:09 -08008#include <sys/socket.h>
Tom Joseph7fd26dd2017-03-14 15:26:26 +05309#include <systemd/sd-daemon.h>
Vernon Mauery9e801a22018-10-12 13:20:49 -070010
Vernon Mauerycbccb052018-10-24 13:52:22 -070011#include <boost/asio/io_context.hpp>
Ed Tanous07bb0952020-08-17 23:32:38 -070012#include <boost/asio/signal_set.hpp>
Tom Joseph7fd26dd2017-03-14 15:26:26 +053013#include <phosphor-logging/log.hpp>
Vernon Mauerycbccb052018-10-24 13:52:22 -070014#include <sdbusplus/asio/sd_event.hpp>
Alvin Wang8c0bf982019-10-21 10:30:07 +080015#include <user_channel/channel_layer.hpp>
Tom Joseph7fd26dd2017-03-14 15:26:26 +053016
17namespace eventloop
18{
19using namespace phosphor::logging;
20
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 {
Vernon Mauery7a0142c2018-11-09 08:38:16 -080034 log<level::ERR>("Executing the IPMI message failed",
35 entry("EXCEPTION=%s", e.what()));
Tom Joseph7fd26dd2017-03-14 15:26:26 +053036 }
Vernon Mauery7a0142c2018-11-09 08:38:16 -080037}
Tom Joseph7fd26dd2017-03-14 15:26:26 +053038
Vernon Mauery7a0142c2018-11-09 08:38:16 -080039void EventLoop::startRmcpReceive()
40{
41 udpSocket->async_wait(boost::asio::socket_base::wait_read,
42 [this](const boost::system::error_code& ec) {
43 if (!ec)
44 {
45 io->post([this]() { startRmcpReceive(); });
46 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
59 sdbusplus::bus::bus bus{ipmid_get_sd_bus_connection()};
60 // 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 }
71 catch (std::exception& e)
72 {
73 log<level::ERR>("getVLANID: failed to execute/read GetSubTree");
74 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 }
125 catch (std::exception& e)
126 {
127 log<level::ERR>("getVLANID: failed to execute/read VLAN Id");
128 return 0;
129 }
130
131 vlanid = std::get<uint32_t>(value);
132 if ((vlanid & VLAN_VALUE_MASK) != vlanid)
133 {
134 log<level::ERR>("networkd returned an invalid vlan",
135 entry("VLAN=%", vlanid));
136 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);
159 log<level::DEBUG>("This channel has VLAN id",
160 entry("VLANID=%d", vlanid));
161 }
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 {
167 log<level::ERR>("Too many file descriptors received");
168 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 {
175 log<level::ERR>("Failed to set up systemd-passed socket");
176 return EXIT_FAILURE;
177 }
178 udpSocket = std::make_shared<boost::asio::ip::udp::socket>(
179 *io, boost::asio::ip::udp::v6(), openFd);
180 }
181 else
182 {
183 // asio does not natively offer a way to bind to an interface
184 // so it must be done in steps
185 boost::asio::ip::udp::endpoint ep(boost::asio::ip::udp::v6(), reqPort);
186 udpSocket = std::make_shared<boost::asio::ip::udp::socket>(*io);
187 udpSocket->open(ep.protocol());
188 // bind
189 udpSocket->set_option(
190 boost::asio::ip::udp::socket::reuse_address(true));
191 udpSocket->bind(ep);
192 }
193 // SO_BINDTODEVICE
194 char nameout[IFNAMSIZ];
195 unsigned int lenout = sizeof(nameout);
196 if ((::getsockopt(udpSocket->native_handle(), SOL_SOCKET, SO_BINDTODEVICE,
197 nameout, &lenout) == -1))
198 {
199 log<level::ERR>("Failed to read bound device",
200 entry("ERROR=%s", strerror(errno)));
201 }
202 if (iface != nameout && iface != unboundIface)
203 {
204 // SO_BINDTODEVICE
205 if ((::setsockopt(udpSocket->native_handle(), SOL_SOCKET,
206 SO_BINDTODEVICE, iface.c_str(),
207 iface.size() + 1) == -1))
208 {
209 log<level::ERR>("Failed to bind to requested interface",
210 entry("ERROR=%s", strerror(errno)));
211 return EXIT_FAILURE;
212 }
Alvin Wang8c0bf982019-10-21 10:30:07 +0800213 log<level::INFO>("Bind to interfae",
214 entry("INTERFACE=%s", iface.c_str()));
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 {
232 log<level::ERR>("Failed to acquire D-Bus name",
233 entry("NAME=%s", busName.c_str()),
234 entry("ERROR=%s", e.what()));
235 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);
Vernon Mauery7a0142c2018-11-09 08:38:16 -0800244 signals.async_wait(
245 [this](const boost::system::error_code& error, 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