blob: 2ad5d2e348ad29dca06ebb44ffb888bacd087ad4 [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>
Tom Joseph7fd26dd2017-03-14 15:26:26 +053012#include <phosphor-logging/log.hpp>
Vernon Mauerycbccb052018-10-24 13:52:22 -070013#include <sdbusplus/asio/sd_event.hpp>
Alvin Wang8c0bf982019-10-21 10:30:07 +080014#include <user_channel/channel_layer.hpp>
Tom Joseph7fd26dd2017-03-14 15:26:26 +053015
16namespace eventloop
17{
18using namespace phosphor::logging;
19
Vernon Mauery7a0142c2018-11-09 08:38:16 -080020void EventLoop::handleRmcpPacket()
Tom Joseph7fd26dd2017-03-14 15:26:26 +053021{
Tom Joseph7fd26dd2017-03-14 15:26:26 +053022 try
23 {
Vernon Mauery7a0142c2018-11-09 08:38:16 -080024 auto channelPtr = std::make_shared<udpsocket::Channel>(udpSocket);
Tom Joseph7fd26dd2017-03-14 15:26:26 +053025
26 // Initialize the Message Handler with the socket channel
Vernon Mauery8d6f2002018-11-07 09:55:53 -080027 auto msgHandler = std::make_shared<message::Handler>(channelPtr, io);
Tom Joseph7fd26dd2017-03-14 15:26:26 +053028
Vernon Mauery8d6f2002018-11-07 09:55:53 -080029 msgHandler->processIncoming();
Tom Joseph7fd26dd2017-03-14 15:26:26 +053030 }
Vernon Mauery7a0142c2018-11-09 08:38:16 -080031 catch (const std::exception& e)
Tom Joseph7fd26dd2017-03-14 15:26:26 +053032 {
Vernon Mauery7a0142c2018-11-09 08:38:16 -080033 log<level::ERR>("Executing the IPMI message failed",
34 entry("EXCEPTION=%s", e.what()));
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 {
44 io->post([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
58 sdbusplus::bus::bus bus{ipmid_get_sd_bus_connection()};
59 // 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 }
70 catch (std::exception& e)
71 {
72 log<level::ERR>("getVLANID: failed to execute/read GetSubTree");
73 return 0;
74 }
75
76 std::string ifService, logicalPath;
77 for (const auto& [path, impls] : objs)
78 {
79 if (path.find(channel) == path.npos)
80 {
81 continue;
82 }
83 for (const auto& [service, intfs] : impls)
84 {
85 bool vlan = false;
86 bool ethernet = false;
87 for (const auto& intf : intfs)
88 {
89 if (intf == INTF_VLAN)
90 {
91 vlan = true;
92 }
93 else if (intf == INTF_ETHERNET)
94 {
95 ethernet = true;
96 }
97 }
98 if (ifService.empty() && (vlan || ethernet))
99 {
100 ifService = service;
101 }
102 if (logicalPath.empty() && vlan)
103 {
104 logicalPath = path;
105 }
106 }
107 }
108
109 // VLAN devices will always have a separate logical object
110 if (logicalPath.empty())
111 {
112 return 0;
113 }
114
115 Value value;
116 auto method = bus.new_method_call(ifService.c_str(), logicalPath.c_str(),
117 PROP_INTF, METHOD_GET);
118 method.append(INTF_VLAN, "Id");
119 try
120 {
121 auto method_reply = bus.call(method);
122 method_reply.read(value);
123 }
124 catch (std::exception& e)
125 {
126 log<level::ERR>("getVLANID: failed to execute/read VLAN Id");
127 return 0;
128 }
129
130 vlanid = std::get<uint32_t>(value);
131 if ((vlanid & VLAN_VALUE_MASK) != vlanid)
132 {
133 log<level::ERR>("networkd returned an invalid vlan",
134 entry("VLAN=%", vlanid));
135 return 0;
136 }
137
138 return vlanid;
139}
140
141int EventLoop::setupSocket(std::shared_ptr<sdbusplus::asio::connection>& bus,
142 std::string channel, uint16_t reqPort)
143{
144 std::string iface = channel;
145 static constexpr const char* unboundIface = "rmcpp";
146 if (channel == "")
147 {
148 iface = channel = unboundIface;
149 }
150 else
151 {
152 // If VLANID of this channel is set, bind the socket to this
153 // VLAN logic device
154 auto vlanid = getVLANID(channel);
155 if (vlanid)
156 {
157 iface = iface + "." + std::to_string(vlanid);
158 log<level::DEBUG>("This channel has VLAN id",
159 entry("VLANID=%d", vlanid));
160 }
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 {
166 log<level::ERR>("Too many file descriptors received");
167 return EXIT_FAILURE;
168 }
169 if (listensFdCount == 1)
170 {
171 int openFd = SD_LISTEN_FDS_START;
172 if (!sd_is_socket(openFd, AF_UNSPEC, SOCK_DGRAM, -1))
173 {
174 log<level::ERR>("Failed to set up systemd-passed socket");
175 return EXIT_FAILURE;
176 }
177 udpSocket = std::make_shared<boost::asio::ip::udp::socket>(
178 *io, boost::asio::ip::udp::v6(), openFd);
179 }
180 else
181 {
182 // asio does not natively offer a way to bind to an interface
183 // so it must be done in steps
184 boost::asio::ip::udp::endpoint ep(boost::asio::ip::udp::v6(), reqPort);
185 udpSocket = std::make_shared<boost::asio::ip::udp::socket>(*io);
186 udpSocket->open(ep.protocol());
187 // bind
188 udpSocket->set_option(
189 boost::asio::ip::udp::socket::reuse_address(true));
190 udpSocket->bind(ep);
191 }
192 // SO_BINDTODEVICE
193 char nameout[IFNAMSIZ];
194 unsigned int lenout = sizeof(nameout);
195 if ((::getsockopt(udpSocket->native_handle(), SOL_SOCKET, SO_BINDTODEVICE,
196 nameout, &lenout) == -1))
197 {
198 log<level::ERR>("Failed to read bound device",
199 entry("ERROR=%s", strerror(errno)));
200 }
201 if (iface != nameout && iface != unboundIface)
202 {
203 // SO_BINDTODEVICE
204 if ((::setsockopt(udpSocket->native_handle(), SOL_SOCKET,
205 SO_BINDTODEVICE, iface.c_str(),
206 iface.size() + 1) == -1))
207 {
208 log<level::ERR>("Failed to bind to requested interface",
209 entry("ERROR=%s", strerror(errno)));
210 return EXIT_FAILURE;
211 }
Alvin Wang8c0bf982019-10-21 10:30:07 +0800212 log<level::INFO>("Bind to interfae",
213 entry("INTERFACE=%s", iface.c_str()));
Vernon Maueryd92bc322019-03-15 15:24:30 -0700214 }
215 // cannot be constexpr because it gets passed by address
216 const int option_enabled = 1;
217 // common socket stuff; set options to get packet info (DST addr)
218 ::setsockopt(udpSocket->native_handle(), IPPROTO_IP, IP_PKTINFO,
219 &option_enabled, sizeof(option_enabled));
220 ::setsockopt(udpSocket->native_handle(), IPPROTO_IPV6, IPV6_RECVPKTINFO,
221 &option_enabled, sizeof(option_enabled));
222
223 // set the dbus name
Alvin Wang8c0bf982019-10-21 10:30:07 +0800224 std::string busName = "xyz.openbmc_project.Ipmi.Channel." + channel;
Vernon Maueryd92bc322019-03-15 15:24:30 -0700225 try
226 {
227 bus->request_name(busName.c_str());
228 }
229 catch (const std::exception& e)
230 {
231 log<level::ERR>("Failed to acquire D-Bus name",
232 entry("NAME=%s", busName.c_str()),
233 entry("ERROR=%s", e.what()));
234 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);
Vernon Mauery7a0142c2018-11-09 08:38:16 -0800243 signals.async_wait(
244 [this](const boost::system::error_code& error, 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