blob: 61065ad966c849d15df7799d02206a33bd472a2b [file] [log] [blame]
Johnathan Manteyaffadb52019-10-07 10:13:53 -07001#include "app/channel.hpp"
2
Patrick Venture0b02be92018-08-31 11:55:55 -07003#include <arpa/inet.h>
William A. Kennington IIIc514d872019-04-06 18:19:38 -07004#include <netinet/ether.h>
Patrick Venture0b02be92018-08-31 11:55:55 -07005
William A. Kennington IIIc514d872019-04-06 18:19:38 -07006#include <array>
7#include <bitset>
8#include <cinttypes>
9#include <cstdint>
10#include <cstring>
Johnathan Manteyaffadb52019-10-07 10:13:53 -070011#include <fstream>
William A. Kennington IIIc514d872019-04-06 18:19:38 -070012#include <functional>
Vernon Mauerye08fbff2019-04-03 09:19:34 -070013#include <ipmid/api.hpp>
William A. Kennington IIIc514d872019-04-06 18:19:38 -070014#include <ipmid/message.hpp>
15#include <ipmid/message/types.hpp>
16#include <ipmid/types.hpp>
Vernon Mauery6a98fe72019-03-11 15:57:48 -070017#include <ipmid/utils.hpp>
William A. Kennington IIIc514d872019-04-06 18:19:38 -070018#include <optional>
Patrick Venture3a5071a2018-09-12 13:27:42 -070019#include <phosphor-logging/elog-errors.hpp>
William A. Kennington IIIc514d872019-04-06 18:19:38 -070020#include <phosphor-logging/elog.hpp>
Patrick Venture3a5071a2018-09-12 13:27:42 -070021#include <phosphor-logging/log.hpp>
William A. Kennington IIIc514d872019-04-06 18:19:38 -070022#include <sdbusplus/bus.hpp>
23#include <sdbusplus/exception.hpp>
tomjose26e17732016-03-03 08:52:51 -060024#include <string>
William A. Kennington IIIc514d872019-04-06 18:19:38 -070025#include <string_view>
26#include <type_traits>
27#include <unordered_map>
28#include <unordered_set>
29#include <user_channel/channel_layer.hpp>
30#include <utility>
31#include <vector>
Patrick Venture3a5071a2018-09-12 13:27:42 -070032#include <xyz/openbmc_project/Common/error.hpp>
William A. Kennington IIIc514d872019-04-06 18:19:38 -070033#include <xyz/openbmc_project/Network/IP/server.hpp>
William A. Kennington III4bbc3db2019-04-15 00:02:10 -070034#include <xyz/openbmc_project/Network/Neighbor/server.hpp>
Patrick Venture3a5071a2018-09-12 13:27:42 -070035
William A. Kennington IIIc514d872019-04-06 18:19:38 -070036using phosphor::logging::commit;
37using phosphor::logging::elog;
38using phosphor::logging::entry;
39using phosphor::logging::level;
40using phosphor::logging::log;
41using sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure;
42using sdbusplus::xyz::openbmc_project::Network::server::IP;
William A. Kennington III4bbc3db2019-04-15 00:02:10 -070043using sdbusplus::xyz::openbmc_project::Network::server::Neighbor;
William A. Kennington IIIc514d872019-04-06 18:19:38 -070044
Johnathan Manteyaffadb52019-10-07 10:13:53 -070045namespace cipher
46{
47
48std::vector<uint8_t> getCipherList()
49{
50 std::vector<uint8_t> cipherList;
51
52 std::ifstream jsonFile(cipher::configFile);
53 if (!jsonFile.is_open())
54 {
55 log<level::ERR>("Channel Cipher suites file not found");
56 elog<InternalFailure>();
57 }
58
59 auto data = Json::parse(jsonFile, nullptr, false);
60 if (data.is_discarded())
61 {
62 log<level::ERR>("Parsing channel cipher suites JSON failed");
63 elog<InternalFailure>();
64 }
65
66 // Byte 1 is reserved
67 cipherList.push_back(0x00);
68
69 for (const auto& record : data)
70 {
71 cipherList.push_back(record.value(cipher, 0));
72 }
73
74 return cipherList;
75}
76} // namespace cipher
77
78namespace ipmi
79{
80namespace transport
81{
82
William A. Kennington IIIc514d872019-04-06 18:19:38 -070083// LAN Handler specific response codes
84constexpr Cc ccParamNotSupported = 0x80;
85constexpr Cc ccParamSetLocked = 0x81;
86constexpr Cc ccParamReadOnly = 0x82;
87
88// VLANs are a 12-bit value
89constexpr uint16_t VLAN_VALUE_MASK = 0x0fff;
90constexpr uint16_t VLAN_ENABLE_FLAG = 0x8000;
91
92// D-Bus Network Daemon definitions
93constexpr auto PATH_ROOT = "/xyz/openbmc_project/network";
94constexpr auto PATH_SYSTEMCONFIG = "/xyz/openbmc_project/network/config";
95
96constexpr auto INTF_SYSTEMCONFIG =
97 "xyz.openbmc_project.Network.SystemConfiguration";
98constexpr auto INTF_ETHERNET = "xyz.openbmc_project.Network.EthernetInterface";
99constexpr auto INTF_IP = "xyz.openbmc_project.Network.IP";
100constexpr auto INTF_IP_CREATE = "xyz.openbmc_project.Network.IP.Create";
101constexpr auto INTF_MAC = "xyz.openbmc_project.Network.MACAddress";
William A. Kennington III4bbc3db2019-04-15 00:02:10 -0700102constexpr auto INTF_NEIGHBOR = "xyz.openbmc_project.Network.Neighbor";
103constexpr auto INTF_NEIGHBOR_CREATE_STATIC =
104 "xyz.openbmc_project.Network.Neighbor.CreateStatic";
William A. Kennington IIIc514d872019-04-06 18:19:38 -0700105constexpr auto INTF_VLAN = "xyz.openbmc_project.Network.VLAN";
106constexpr auto INTF_VLAN_CREATE = "xyz.openbmc_project.Network.VLAN.Create";
107
108/** @brief Generic paramters for different address families */
109template <int family>
110struct AddrFamily
111{
112};
113
114/** @brief Parameter specialization for IPv4 */
115template <>
116struct AddrFamily<AF_INET>
117{
118 using addr = in_addr;
119 static constexpr auto protocol = IP::Protocol::IPv4;
120 static constexpr size_t maxStrLen = INET6_ADDRSTRLEN;
121 static constexpr uint8_t defaultPrefix = 32;
122 static constexpr char propertyGateway[] = "DefaultGateway";
123};
124
125/** @brief Valid address origins for IPv4 */
126const std::unordered_set<IP::AddressOrigin> originsV4 = {
127 IP::AddressOrigin::Static,
128 IP::AddressOrigin::DHCP,
129};
130
131/** @brief Interface IP Address configuration parameters */
132template <int family>
133struct IfAddr
134{
135 std::string path;
136 typename AddrFamily<family>::addr address;
137 IP::AddressOrigin origin;
138 uint8_t prefix;
139};
140
William A. Kennington III4bbc3db2019-04-15 00:02:10 -0700141/** @brief Interface Neighbor configuration parameters */
142template <int family>
143struct IfNeigh
144{
145 std::string path;
146 typename AddrFamily<family>::addr ip;
147 ether_addr mac;
148};
149
William A. Kennington IIIc514d872019-04-06 18:19:38 -0700150/** @brief IPMI LAN Parameters */
151enum class LanParam : uint8_t
152{
153 SetStatus = 0,
154 AuthSupport = 1,
155 AuthEnables = 2,
156 IP = 3,
157 IPSrc = 4,
158 MAC = 5,
159 SubnetMask = 6,
160 Gateway1 = 12,
William A. Kennington III4bbc3db2019-04-15 00:02:10 -0700161 Gateway1MAC = 13,
William A. Kennington IIIc514d872019-04-06 18:19:38 -0700162 VLANId = 20,
163 CiphersuiteSupport = 22,
164 CiphersuiteEntries = 23,
165};
166
Johnathan Manteyb87034e2019-09-16 10:50:50 -0700167static constexpr uint8_t oemCmdStart = 192;
168static constexpr uint8_t oemCmdEnd = 255;
169
William A. Kennington IIIc514d872019-04-06 18:19:38 -0700170/** @brief IPMI IP Origin Types */
171enum class IPSrc : uint8_t
172{
173 Unspecified = 0,
174 Static = 1,
175 DHCP = 2,
176 BIOS = 3,
177 BMC = 4,
178};
179
180/** @brief IPMI Set Status */
181enum class SetStatus : uint8_t
182{
183 Complete = 0,
184 InProgress = 1,
185 Commit = 2,
186};
187
William A. Kennington III4bbc3db2019-04-15 00:02:10 -0700188/** @brief A trivial helper used to determine if two PODs are equal
189 *
190 * @params[in] a - The first object to compare
191 * @params[in] b - The second object to compare
192 * @return True if the objects are the same bytewise
193 */
194template <typename T>
195bool equal(const T& a, const T& b)
196{
197 static_assert(std::is_trivially_copyable_v<T>);
198 return std::memcmp(&a, &b, sizeof(T)) == 0;
199}
200
William A. Kennington IIIc514d872019-04-06 18:19:38 -0700201/** @brief Copies bytes from an array into a trivially copyable container
202 *
203 * @params[out] t - The container receiving the data
204 * @params[in] bytes - The data to copy
205 */
206template <size_t N, typename T>
207void copyInto(T& t, const std::array<uint8_t, N>& bytes)
208{
209 static_assert(std::is_trivially_copyable_v<T>);
210 static_assert(N == sizeof(T));
211 std::memcpy(&t, bytes.data(), bytes.size());
Patrick Venturec7c1c3c2017-11-15 14:29:18 -0800212}
213
William A. Kennington IIIc514d872019-04-06 18:19:38 -0700214/** @brief Gets a generic view of the bytes in the input container
215 *
216 * @params[in] t - The data to reference
217 * @return A string_view referencing the bytes in the container
218 */
219template <typename T>
220std::string_view dataRef(const T& t)
tomjose26e17732016-03-03 08:52:51 -0600221{
William A. Kennington IIIc514d872019-04-06 18:19:38 -0700222 static_assert(std::is_trivially_copyable_v<T>);
223 return {reinterpret_cast<const char*>(&t), sizeof(T)};
224}
Ratan Gupta533d03b2017-07-30 10:39:22 +0530225
William A. Kennington IIIc514d872019-04-06 18:19:38 -0700226/** @brief The dbus parameters for the interface corresponding to a channel
227 * This helps reduce the number of mapper lookups we need for each
228 * query and simplifies finding the VLAN interface if needed.
229 */
230struct ChannelParams
231{
232 /** @brief The channel ID */
233 int id;
234 /** @brief channel name for the interface */
235 std::string ifname;
236 /** @brief Name of the service on the bus */
237 std::string service;
238 /** @brief Lower level adapter path that is guaranteed to not be a VLAN */
239 std::string ifPath;
240 /** @brief Logical adapter path used for address assignment */
241 std::string logicalPath;
242};
243
244/** @brief Determines the ethernet interface name corresponding to a channel
245 * Tries to map a VLAN object first so that the address information
246 * is accurate. Otherwise it gets the standard ethernet interface.
247 *
248 * @param[in] bus - The bus object used for lookups
249 * @param[in] channel - The channel id corresponding to an ethernet interface
250 * @return Ethernet interface service and object path if it exists
251 */
252std::optional<ChannelParams> maybeGetChannelParams(sdbusplus::bus::bus& bus,
253 uint8_t channel)
254{
255 auto ifname = getChannelName(channel);
256 if (ifname.empty())
Patrick Venturec7c1c3c2017-11-15 14:29:18 -0800257 {
William A. Kennington IIIc514d872019-04-06 18:19:38 -0700258 return std::nullopt;
Patrick Venturec7c1c3c2017-11-15 14:29:18 -0800259 }
Patrick Venturec7c1c3c2017-11-15 14:29:18 -0800260
William A. Kennington IIIc514d872019-04-06 18:19:38 -0700261 // Enumerate all VLAN + ETHERNET interfaces
262 auto req = bus.new_method_call(MAPPER_BUS_NAME, MAPPER_OBJ, MAPPER_INTF,
263 "GetSubTree");
264 req.append(PATH_ROOT, 0,
265 std::vector<std::string>{INTF_VLAN, INTF_ETHERNET});
266 auto reply = bus.call(req);
267 ObjectTree objs;
268 reply.read(objs);
269
270 ChannelParams params;
271 for (const auto& [path, impls] : objs)
272 {
273 if (path.find(ifname) == path.npos)
274 {
275 continue;
276 }
277 for (const auto& [service, intfs] : impls)
278 {
279 bool vlan = false;
280 bool ethernet = false;
281 for (const auto& intf : intfs)
282 {
283 if (intf == INTF_VLAN)
284 {
285 vlan = true;
286 }
287 else if (intf == INTF_ETHERNET)
288 {
289 ethernet = true;
290 }
291 }
292 if (params.service.empty() && (vlan || ethernet))
293 {
294 params.service = service;
295 }
296 if (params.ifPath.empty() && !vlan && ethernet)
297 {
298 params.ifPath = path;
299 }
300 if (params.logicalPath.empty() && vlan)
301 {
302 params.logicalPath = path;
303 }
304 }
305 }
306
307 // We must have a path for the underlying interface
308 if (params.ifPath.empty())
309 {
310 return std::nullopt;
311 }
312 // We don't have a VLAN so the logical path is the same
313 if (params.logicalPath.empty())
314 {
315 params.logicalPath = params.ifPath;
316 }
317
318 params.id = channel;
319 params.ifname = std::move(ifname);
320 return std::move(params);
321}
322
323/** @brief A trivial helper around maybeGetChannelParams() that throws an
324 * exception when it is unable to acquire parameters for the channel.
325 *
326 * @param[in] bus - The bus object used for lookups
327 * @param[in] channel - The channel id corresponding to an ethernet interface
328 * @return Ethernet interface service and object path
329 */
330ChannelParams getChannelParams(sdbusplus::bus::bus& bus, uint8_t channel)
331{
332 auto params = maybeGetChannelParams(bus, channel);
333 if (!params)
334 {
335 log<level::ERR>("Failed to get channel params",
336 entry("CHANNEL=%" PRIu8, channel));
337 elog<InternalFailure>();
338 }
339 return std::move(*params);
340}
341
342/** @brief Wraps the phosphor logging method to insert some additional metadata
343 *
344 * @param[in] params - The parameters for the channel
345 * ...
346 */
347template <auto level, typename... Args>
348auto logWithChannel(const ChannelParams& params, Args&&... args)
349{
350 return log<level>(std::forward<Args>(args)...,
351 entry("CHANNEL=%d", params.id),
352 entry("IFNAME=%s", params.ifname.c_str()));
353}
354template <auto level, typename... Args>
355auto logWithChannel(const std::optional<ChannelParams>& params, Args&&... args)
356{
357 if (params)
358 {
359 return logWithChannel<level>(*params, std::forward<Args>(args)...);
360 }
361 return log<level>(std::forward<Args>(args)...);
362}
363
364/** @brief Trivializes using parameter getter functions by providing a bus
365 * and channel parameters automatically.
366 *
367 * @param[in] channel - The channel id corresponding to an ethernet interface
368 * ...
369 */
370template <auto func, typename... Args>
371auto channelCall(uint8_t channel, Args&&... args)
372{
373 sdbusplus::bus::bus bus(ipmid_get_sd_bus_connection());
374 auto params = getChannelParams(bus, channel);
375 return std::invoke(func, bus, params, std::forward<Args>(args)...);
376}
377
378/** @brief Determines if the ethernet interface is using DHCP
379 *
380 * @param[in] bus - The bus object used for lookups
381 * @param[in] params - The parameters for the channel
382 * @return True if DHCP is enabled, false otherwise
383 */
384bool getDHCPProperty(sdbusplus::bus::bus& bus, const ChannelParams& params)
385{
386 return std::get<bool>(getDbusProperty(
387 bus, params.service, params.logicalPath, INTF_ETHERNET, "DHCPEnabled"));
388}
389
390/** @brief Sets the system value for DHCP on the given interface
391 *
392 * @param[in] bus - The bus object used for lookups
393 * @param[in] params - The parameters for the channel
394 * @param[in] on - Whether or not to enable DHCP
395 */
396void setDHCPProperty(sdbusplus::bus::bus& bus, const ChannelParams& params,
397 bool on)
398{
399 setDbusProperty(bus, params.service, params.logicalPath, INTF_ETHERNET,
400 "DHCPEnabled", on);
401}
402
403/** @brief Converts a human readable MAC string into MAC bytes
404 *
405 * @param[in] mac - The MAC string
406 * @return MAC in bytes
407 */
408ether_addr stringToMAC(const char* mac)
409{
410 const ether_addr* ret = ether_aton(mac);
411 if (ret == nullptr)
412 {
413 log<level::ERR>("Invalid MAC Address", entry("MAC=%s", mac));
414 elog<InternalFailure>();
415 }
416 return *ret;
417}
418
419/** @brief Determines the MAC of the ethernet interface
420 *
421 * @param[in] bus - The bus object used for lookups
422 * @param[in] params - The parameters for the channel
423 * @return The configured mac address
424 */
425ether_addr getMACProperty(sdbusplus::bus::bus& bus, const ChannelParams& params)
426{
427 auto macStr = std::get<std::string>(getDbusProperty(
428 bus, params.service, params.ifPath, INTF_MAC, "MACAddress"));
429 return stringToMAC(macStr.c_str());
430}
431
432/** @brief Sets the system value for MAC address on the given interface
433 *
434 * @param[in] bus - The bus object used for lookups
435 * @param[in] params - The parameters for the channel
436 * @param[in] mac - MAC address to apply
437 */
438void setMACProperty(sdbusplus::bus::bus& bus, const ChannelParams& params,
439 const ether_addr& mac)
440{
441 std::string macStr = ether_ntoa(&mac);
442 setDbusProperty(bus, params.service, params.ifPath, INTF_MAC, "MACAddress",
443 macStr);
444}
445
446/** @brief Turns an IP address string into the network byte order form
447 * NOTE: This version strictly validates family matches
448 *
449 * @param[in] address - The string form of the address
450 * @return A network byte order address or none if conversion failed
451 */
452template <int family>
453std::optional<typename AddrFamily<family>::addr>
454 maybeStringToAddr(const char* address)
455{
456 typename AddrFamily<family>::addr ret;
457 if (inet_pton(family, address, &ret) == 1)
458 {
459 return ret;
460 }
461 return std::nullopt;
462}
463
464/** @brief Turns an IP address string into the network byte order form
465 * NOTE: This version strictly validates family matches
466 *
467 * @param[in] address - The string form of the address
468 * @return A network byte order address
469 */
470template <int family>
471typename AddrFamily<family>::addr stringToAddr(const char* address)
472{
473 auto ret = maybeStringToAddr<family>(address);
474 if (!ret)
475 {
476 log<level::ERR>("Failed to convert IP Address",
477 entry("FAMILY=%d", family),
478 entry("ADDRESS=%s", address));
479 elog<InternalFailure>();
480 }
481 return *ret;
482}
483
484/** @brief Turns an IP address in network byte order into a string
485 *
486 * @param[in] address - The string form of the address
487 * @return A network byte order address
488 */
489template <int family>
490std::string addrToString(const typename AddrFamily<family>::addr& address)
491{
492 std::string ret(AddrFamily<family>::maxStrLen, '\0');
493 inet_ntop(family, &address, ret.data(), ret.size());
494 ret.resize(strlen(ret.c_str()));
495 return ret;
496}
497
498/** @brief Retrieves the current gateway for the address family on the system
499 * NOTE: The gateway is currently system wide and not per channel
500 *
501 * @param[in] bus - The bus object used for lookups
502 * @param[in] params - The parameters for the channel
503 * @return An address representing the gateway address if it exists
504 */
505template <int family>
506std::optional<typename AddrFamily<family>::addr>
507 getGatewayProperty(sdbusplus::bus::bus& bus, const ChannelParams& params)
508{
509 auto gatewayStr = std::get<std::string>(getDbusProperty(
510 bus, params.service, PATH_SYSTEMCONFIG, INTF_SYSTEMCONFIG,
511 AddrFamily<family>::propertyGateway));
512 if (gatewayStr.empty())
513 {
514 return std::nullopt;
515 }
516 return stringToAddr<family>(gatewayStr.c_str());
517}
518
William A. Kennington IIIc514d872019-04-06 18:19:38 -0700519/** @brief A lazy lookup mechanism for iterating over object properties stored
520 * in DBus. This will only perform the object lookup when needed, and
521 * retains a cache of previous lookups to speed up future iterations.
522 */
523class ObjectLookupCache
524{
525 public:
526 using PropertiesCache = std::unordered_map<std::string, PropertyMap>;
527
528 /** @brief Creates a new ObjectLookupCache for the interface on the bus
529 * NOTE: The inputs to this object must outlive the object since
530 * they are only referenced by it.
531 *
532 * @param[in] bus - The bus object used for lookups
533 * @param[in] params - The parameters for the channel
534 * @param[in] intf - The interface we are looking up
535 */
536 ObjectLookupCache(sdbusplus::bus::bus& bus, const ChannelParams& params,
537 const char* intf) :
538 bus(bus),
539 params(params), intf(intf),
540 objs(getAllDbusObjects(bus, params.logicalPath, intf, ""))
541 {
542 }
543
544 class iterator : public ObjectTree::const_iterator
545 {
546 public:
547 using value_type = PropertiesCache::value_type;
548
549 iterator(ObjectTree::const_iterator it, ObjectLookupCache& container) :
550 ObjectTree::const_iterator(it), container(container),
551 ret(container.cache.end())
552 {
553 }
554 value_type& operator*()
555 {
556 ret = container.get(ObjectTree::const_iterator::operator*().first);
557 return *ret;
558 }
559 value_type* operator->()
560 {
561 return &operator*();
562 }
563
564 private:
565 ObjectLookupCache& container;
566 PropertiesCache::iterator ret;
567 };
568
569 iterator begin() noexcept
570 {
571 return iterator(objs.begin(), *this);
572 }
573
574 iterator end() noexcept
575 {
576 return iterator(objs.end(), *this);
577 }
578
579 private:
580 sdbusplus::bus::bus& bus;
581 const ChannelParams& params;
582 const char* const intf;
583 const ObjectTree objs;
584 PropertiesCache cache;
585
586 /** @brief Gets a cached copy of the object properties if possible
587 * Otherwise performs a query on DBus to look them up
588 *
589 * @param[in] path - The object path to lookup
590 * @return An iterator for the specified object path + properties
591 */
592 PropertiesCache::iterator get(const std::string& path)
593 {
594 auto it = cache.find(path);
595 if (it != cache.end())
596 {
597 return it;
598 }
599 auto properties = getAllDbusProperties(bus, params.service, path, intf);
600 return cache.insert({path, std::move(properties)}).first;
601 }
602};
603
604/** @brief Searches the ip object lookup cache for an address matching
605 * the input parameters. NOTE: The index lacks stability across address
606 * changes since the network daemon has no notion of stable indicies.
607 *
608 * @param[in] bus - The bus object used for lookups
609 * @param[in] params - The parameters for the channel
610 * @param[in] idx - The index of the desired address on the interface
611 * @param[in] origins - The allowed origins for the address objects
612 * @param[in] ips - The object lookup cache holding all of the address info
613 * @return The address and prefix if it was found
614 */
615template <int family>
616std::optional<IfAddr<family>>
617 findIfAddr(sdbusplus::bus::bus& bus, const ChannelParams& params,
618 uint8_t idx,
619 const std::unordered_set<IP::AddressOrigin>& origins,
620 ObjectLookupCache& ips)
621{
622 for (const auto& [path, properties] : ips)
623 {
624 const auto& addrStr = std::get<std::string>(properties.at("Address"));
625 auto addr = maybeStringToAddr<family>(addrStr.c_str());
626 if (!addr)
627 {
628 continue;
629 }
630
631 IP::AddressOrigin origin = IP::convertAddressOriginFromString(
632 std::get<std::string>(properties.at("Origin")));
633 if (origins.find(origin) == origins.end())
634 {
635 continue;
636 }
637
638 if (idx > 0)
639 {
640 idx--;
641 continue;
642 }
643
644 IfAddr<family> ifaddr;
645 ifaddr.path = path;
646 ifaddr.address = *addr;
647 ifaddr.prefix = std::get<uint8_t>(properties.at("PrefixLength"));
648 ifaddr.origin = origin;
649 return std::move(ifaddr);
650 }
651
652 return std::nullopt;
653}
654
655/** @brief Trivial helper around findIfAddr that simplifies calls
656 * for one off lookups. Don't use this if you intend to do multiple
657 * lookups at a time.
658 *
659 * @param[in] bus - The bus object used for lookups
660 * @param[in] params - The parameters for the channel
661 * @param[in] idx - The index of the desired address on the interface
662 * @param[in] origins - The allowed origins for the address objects
663 * @return The address and prefix if it was found
664 */
665template <int family>
666auto getIfAddr(sdbusplus::bus::bus& bus, const ChannelParams& params,
667 uint8_t idx,
668 const std::unordered_set<IP::AddressOrigin>& origins)
669{
670 ObjectLookupCache ips(bus, params, INTF_IP);
671 return findIfAddr<family>(bus, params, idx, origins, ips);
672}
673
674/** @brief Deletes the dbus object. Ignores empty objects or objects that are
675 * missing from the bus.
676 *
677 * @param[in] bus - The bus object used for lookups
678 * @param[in] service - The name of the service
679 * @param[in] path - The path of the object to delete
680 */
681void deleteObjectIfExists(sdbusplus::bus::bus& bus, const std::string& service,
682 const std::string& path)
683{
684 if (path.empty())
685 {
686 return;
687 }
Ratan Guptab8e99552017-07-27 07:07:48 +0530688 try
tomjose26e17732016-03-03 08:52:51 -0600689 {
William A. Kennington IIIc514d872019-04-06 18:19:38 -0700690 auto req = bus.new_method_call(service.c_str(), path.c_str(),
691 ipmi::DELETE_INTERFACE, "Delete");
692 bus.call_noreply(req);
693 }
694 catch (const sdbusplus::exception::SdBusError& e)
695 {
696 if (strcmp(e.name(), "org.freedesktop.DBus.Error.UnknownObject") != 0)
tomjose26e17732016-03-03 08:52:51 -0600697 {
William A. Kennington IIIc514d872019-04-06 18:19:38 -0700698 // We want to rethrow real errors
699 throw;
tomjose26e17732016-03-03 08:52:51 -0600700 }
701 }
tomjose26e17732016-03-03 08:52:51 -0600702}
703
William A. Kennington IIIc514d872019-04-06 18:19:38 -0700704/** @brief Sets the address info configured for the interface
705 * If a previous address path exists then it will be removed
706 * before the new address is added.
707 *
708 * @param[in] bus - The bus object used for lookups
709 * @param[in] params - The parameters for the channel
710 * @param[in] address - The address of the new IP
711 * @param[in] prefix - The prefix of the new IP
712 */
713template <int family>
714void createIfAddr(sdbusplus::bus::bus& bus, const ChannelParams& params,
715 const typename AddrFamily<family>::addr& address,
716 uint8_t prefix)
Tom Josepha30c8d32018-03-22 02:15:03 +0530717{
William A. Kennington IIIc514d872019-04-06 18:19:38 -0700718 auto newreq =
719 bus.new_method_call(params.service.c_str(), params.logicalPath.c_str(),
720 INTF_IP_CREATE, "IP");
721 std::string protocol =
722 sdbusplus::xyz::openbmc_project::Network::server::convertForMessage(
723 AddrFamily<family>::protocol);
724 newreq.append(protocol, addrToString<family>(address), prefix, "");
725 bus.call_noreply(newreq);
726}
Tom Josepha30c8d32018-03-22 02:15:03 +0530727
William A. Kennington IIIc514d872019-04-06 18:19:38 -0700728/** @brief Trivial helper for getting the IPv4 address from getIfAddrs()
729 *
730 * @param[in] bus - The bus object used for lookups
731 * @param[in] params - The parameters for the channel
732 * @return The address and prefix if found
733 */
734auto getIfAddr4(sdbusplus::bus::bus& bus, const ChannelParams& params)
Tom Josepha30c8d32018-03-22 02:15:03 +0530735{
William A. Kennington IIIc514d872019-04-06 18:19:38 -0700736 return getIfAddr<AF_INET>(bus, params, 0, originsV4);
737}
Tom Josepha30c8d32018-03-22 02:15:03 +0530738
William A. Kennington IIIc514d872019-04-06 18:19:38 -0700739/** @brief Reconfigures the IPv4 address info configured for the interface
740 *
741 * @param[in] bus - The bus object used for lookups
742 * @param[in] params - The parameters for the channel
743 * @param[in] address - The new address if specified
744 * @param[in] prefix - The new address prefix if specified
745 */
746void reconfigureIfAddr4(sdbusplus::bus::bus& bus, const ChannelParams& params,
747 const std::optional<in_addr>& address,
748 std::optional<uint8_t> prefix)
749{
750 auto ifaddr = getIfAddr4(bus, params);
751 if (!ifaddr && !address)
Tom Josepha30c8d32018-03-22 02:15:03 +0530752 {
William A. Kennington IIIc514d872019-04-06 18:19:38 -0700753 log<level::ERR>("Missing address for IPv4 assignment");
Tom Josepha30c8d32018-03-22 02:15:03 +0530754 elog<InternalFailure>();
755 }
William A. Kennington IIIc514d872019-04-06 18:19:38 -0700756 uint8_t fallbackPrefix = AddrFamily<AF_INET>::defaultPrefix;
757 if (ifaddr)
Tom Josepha30c8d32018-03-22 02:15:03 +0530758 {
William A. Kennington IIIc514d872019-04-06 18:19:38 -0700759 fallbackPrefix = ifaddr->prefix;
760 deleteObjectIfExists(bus, params.service, ifaddr->path);
761 }
762 createIfAddr<AF_INET>(bus, params, address.value_or(ifaddr->address),
763 prefix.value_or(fallbackPrefix));
764}
765
William A. Kennington III4bbc3db2019-04-15 00:02:10 -0700766template <int family>
767std::optional<IfNeigh<family>>
768 findStaticNeighbor(sdbusplus::bus::bus& bus, const ChannelParams& params,
769 const typename AddrFamily<family>::addr& ip,
770 ObjectLookupCache& neighbors)
771{
772 const auto state =
773 sdbusplus::xyz::openbmc_project::Network::server::convertForMessage(
774 Neighbor::State::Permanent);
775 for (const auto& [path, neighbor] : neighbors)
776 {
777 const auto& ipStr = std::get<std::string>(neighbor.at("IPAddress"));
778 auto neighIP = maybeStringToAddr<family>(ipStr.c_str());
779 if (!neighIP)
780 {
781 continue;
782 }
783 if (!equal(*neighIP, ip))
784 {
785 continue;
786 }
787 if (state != std::get<std::string>(neighbor.at("State")))
788 {
789 continue;
790 }
791
792 IfNeigh<family> ret;
793 ret.path = path;
794 ret.ip = ip;
795 const auto& macStr = std::get<std::string>(neighbor.at("MACAddress"));
796 ret.mac = stringToMAC(macStr.c_str());
797 return std::move(ret);
798 }
799
800 return std::nullopt;
801}
802
803template <int family>
804void createNeighbor(sdbusplus::bus::bus& bus, const ChannelParams& params,
805 const typename AddrFamily<family>::addr& address,
806 const ether_addr& mac)
807{
808 auto newreq =
809 bus.new_method_call(params.service.c_str(), params.logicalPath.c_str(),
810 INTF_NEIGHBOR_CREATE_STATIC, "Neighbor");
811 std::string macStr = ether_ntoa(&mac);
812 newreq.append(addrToString<family>(address), macStr);
813 bus.call_noreply(newreq);
814}
815
816/** @brief Sets the system wide value for the default gateway
817 *
818 * @param[in] bus - The bus object used for lookups
819 * @param[in] params - The parameters for the channel
820 * @param[in] gateway - Gateway address to apply
821 */
822template <int family>
823void setGatewayProperty(sdbusplus::bus::bus& bus, const ChannelParams& params,
824 const typename AddrFamily<family>::addr& address)
825{
826 // Save the old gateway MAC address if it exists so we can recreate it
827 auto gateway = getGatewayProperty<family>(bus, params);
828 std::optional<IfNeigh<family>> neighbor;
829 if (gateway)
830 {
831 ObjectLookupCache neighbors(bus, params, INTF_NEIGHBOR);
832 neighbor = findStaticNeighbor<family>(bus, params, *gateway, neighbors);
833 }
834
835 setDbusProperty(bus, params.service, PATH_SYSTEMCONFIG, INTF_SYSTEMCONFIG,
836 AddrFamily<family>::propertyGateway,
837 addrToString<family>(address));
838
839 // Restore the gateway MAC if we had one
840 if (neighbor)
841 {
842 deleteObjectIfExists(bus, params.service, neighbor->path);
843 createNeighbor<family>(bus, params, address, neighbor->mac);
844 }
845}
846
847template <int family>
848std::optional<IfNeigh<family>> findGatewayNeighbor(sdbusplus::bus::bus& bus,
849 const ChannelParams& params,
850 ObjectLookupCache& neighbors)
851{
852 auto gateway = getGatewayProperty<family>(bus, params);
853 if (!gateway)
854 {
855 return std::nullopt;
856 }
857
858 return findStaticNeighbor<family>(bus, params, *gateway, neighbors);
859}
860
861template <int family>
862std::optional<IfNeigh<family>> getGatewayNeighbor(sdbusplus::bus::bus& bus,
863 const ChannelParams& params)
864{
865 ObjectLookupCache neighbors(bus, params, INTF_NEIGHBOR);
866 return findGatewayNeighbor<family>(bus, params, neighbors);
867}
868
869template <int family>
870void reconfigureGatewayMAC(sdbusplus::bus::bus& bus,
871 const ChannelParams& params, const ether_addr& mac)
872{
873 auto gateway = getGatewayProperty<family>(bus, params);
874 if (!gateway)
875 {
876 log<level::ERR>("Tried to set Gateway MAC without Gateway");
877 elog<InternalFailure>();
878 }
879
880 ObjectLookupCache neighbors(bus, params, INTF_NEIGHBOR);
881 auto neighbor =
882 findStaticNeighbor<family>(bus, params, *gateway, neighbors);
883 if (neighbor)
884 {
885 deleteObjectIfExists(bus, params.service, neighbor->path);
886 }
887
888 createNeighbor<family>(bus, params, *gateway, mac);
889}
890
William A. Kennington IIIc514d872019-04-06 18:19:38 -0700891/** @brief Gets the vlan ID configured on the interface
892 *
893 * @param[in] bus - The bus object used for lookups
894 * @param[in] params - The parameters for the channel
895 * @return VLAN id or the standard 0 for no VLAN
896 */
897uint16_t getVLANProperty(sdbusplus::bus::bus& bus, const ChannelParams& params)
898{
899 // VLAN devices will always have a separate logical object
900 if (params.ifPath == params.logicalPath)
901 {
902 return 0;
903 }
904
905 auto vlan = std::get<uint32_t>(getDbusProperty(
906 bus, params.service, params.logicalPath, INTF_VLAN, "Id"));
907 if ((vlan & VLAN_VALUE_MASK) != vlan)
908 {
909 logWithChannel<level::ERR>(params, "networkd returned an invalid vlan",
910 entry("VLAN=%" PRIu32, vlan));
Tom Josepha30c8d32018-03-22 02:15:03 +0530911 elog<InternalFailure>();
912 }
William A. Kennington IIIc514d872019-04-06 18:19:38 -0700913 return vlan;
Tom Josepha30c8d32018-03-22 02:15:03 +0530914}
915
William A. Kennington IIIc514d872019-04-06 18:19:38 -0700916/** @brief Deletes all of the possible configuration parameters for a channel
917 *
918 * @param[in] bus - The bus object used for lookups
919 * @param[in] params - The parameters for the channel
920 */
921void deconfigureChannel(sdbusplus::bus::bus& bus, ChannelParams& params)
Adriana Kobylak5d6481f2015-10-29 21:44:55 -0500922{
William A. Kennington IIIc514d872019-04-06 18:19:38 -0700923 // Delete all objects associated with the interface
924 auto objreq = bus.new_method_call(MAPPER_BUS_NAME, MAPPER_OBJ, MAPPER_INTF,
925 "GetSubTree");
926 objreq.append(PATH_ROOT, 0, std::vector<std::string>{DELETE_INTERFACE});
927 auto objreply = bus.call(objreq);
928 ObjectTree objs;
929 objreply.read(objs);
930 for (const auto& [path, impls] : objs)
931 {
932 if (path.find(params.ifname) == path.npos)
933 {
934 continue;
935 }
936 for (const auto& [service, intfs] : impls)
937 {
938 deleteObjectIfExists(bus, service, path);
939 }
940 // Update params to reflect the deletion of vlan
941 if (path == params.logicalPath)
942 {
943 params.logicalPath = params.ifPath;
944 }
945 }
946
947 // Clear out any settings on the lower physical interface
948 setDHCPProperty(bus, params, false);
Adriana Kobylak5d6481f2015-10-29 21:44:55 -0500949}
950
William A. Kennington IIIc514d872019-04-06 18:19:38 -0700951/** @brief Creates a new VLAN on the specified interface
952 *
953 * @param[in] bus - The bus object used for lookups
954 * @param[in] params - The parameters for the channel
955 * @param[in] vlan - The id of the new vlan
956 */
957void createVLAN(sdbusplus::bus::bus& bus, ChannelParams& params, uint16_t vlan)
Ratan Guptab8e99552017-07-27 07:07:48 +0530958{
William A. Kennington IIIc514d872019-04-06 18:19:38 -0700959 if (vlan == 0)
Richard Marian Thomaiyar75b480b2019-01-22 00:20:15 +0530960 {
William A. Kennington IIIc514d872019-04-06 18:19:38 -0700961 return;
Richard Marian Thomaiyar75b480b2019-01-22 00:20:15 +0530962 }
963
William A. Kennington IIIc514d872019-04-06 18:19:38 -0700964 auto req = bus.new_method_call(params.service.c_str(), PATH_ROOT,
965 INTF_VLAN_CREATE, "VLAN");
966 req.append(params.ifname, static_cast<uint32_t>(vlan));
967 auto reply = bus.call(req);
968 sdbusplus::message::object_path newPath;
969 reply.read(newPath);
970 params.logicalPath = std::move(newPath);
Richard Marian Thomaiyar75b480b2019-01-22 00:20:15 +0530971}
972
William A. Kennington IIIc514d872019-04-06 18:19:38 -0700973/** @brief Performs the necessary reconfiguration to change the VLAN
974 *
975 * @param[in] bus - The bus object used for lookups
976 * @param[in] params - The parameters for the channel
977 * @param[in] vlan - The new vlan id to use
978 */
979void reconfigureVLAN(sdbusplus::bus::bus& bus, ChannelParams& params,
980 uint16_t vlan)
Adriana Kobylak5d6481f2015-10-29 21:44:55 -0500981{
William A. Kennington IIIc514d872019-04-06 18:19:38 -0700982 // Unfortunatetly we don't have built-in functions to migrate our interface
983 // customizations to new VLAN interfaces, or have some kind of decoupling.
984 // We therefore must retain all of our old information, setup the new VLAN
985 // configuration, then restore the old info.
Nan Li3d0df912016-10-18 19:51:41 +0800986
William A. Kennington IIIc514d872019-04-06 18:19:38 -0700987 // Save info from the old logical interface
988 ObjectLookupCache ips(bus, params, INTF_IP);
989 auto ifaddr4 = findIfAddr<AF_INET>(bus, params, 0, originsV4, ips);
990 auto dhcp = getDHCPProperty(bus, params);
William A. Kennington III4bbc3db2019-04-15 00:02:10 -0700991 ObjectLookupCache neighbors(bus, params, INTF_NEIGHBOR);
992 auto neighbor4 = findGatewayNeighbor<AF_INET>(bus, params, neighbors);
Adriana Kobylak5d6481f2015-10-29 21:44:55 -0500993
William A. Kennington IIIc514d872019-04-06 18:19:38 -0700994 deconfigureChannel(bus, params);
995 createVLAN(bus, params, vlan);
Adriana Kobylak5d6481f2015-10-29 21:44:55 -0500996
William A. Kennington IIIc514d872019-04-06 18:19:38 -0700997 // Re-establish the saved settings
998 setDHCPProperty(bus, params, dhcp);
999 if (ifaddr4)
Patrick Venturec7c1c3c2017-11-15 14:29:18 -08001000 {
William A. Kennington IIIc514d872019-04-06 18:19:38 -07001001 createIfAddr<AF_INET>(bus, params, ifaddr4->address, ifaddr4->prefix);
Patrick Venturec7c1c3c2017-11-15 14:29:18 -08001002 }
William A. Kennington III4bbc3db2019-04-15 00:02:10 -07001003 if (neighbor4)
1004 {
1005 createNeighbor<AF_INET>(bus, params, neighbor4->ip, neighbor4->mac);
1006 }
William A. Kennington IIIc514d872019-04-06 18:19:38 -07001007}
Patrick Venturec7c1c3c2017-11-15 14:29:18 -08001008
William A. Kennington IIIc514d872019-04-06 18:19:38 -07001009/** @brief Turns a prefix into a netmask
1010 *
1011 * @param[in] prefix - The prefix length
1012 * @return The netmask
1013 */
1014in_addr prefixToNetmask(uint8_t prefix)
1015{
1016 if (prefix > 32)
Adriana Kobylak5d6481f2015-10-29 21:44:55 -05001017 {
William A. Kennington IIIc514d872019-04-06 18:19:38 -07001018 log<level::ERR>("Invalid prefix", entry("PREFIX=%" PRIu8, prefix));
1019 elog<InternalFailure>();
1020 }
1021 if (prefix == 0)
1022 {
1023 // Avoids 32-bit lshift by 32 UB
1024 return {};
1025 }
1026 return {htobe32(~UINT32_C(0) << (32 - prefix))};
1027}
1028
1029/** @brief Turns a a netmask into a prefix length
1030 *
1031 * @param[in] netmask - The netmask in byte form
1032 * @return The prefix length
1033 */
1034uint8_t netmaskToPrefix(in_addr netmask)
1035{
1036 uint32_t x = be32toh(netmask.s_addr);
1037 if ((~x & (~x + 1)) != 0)
1038 {
1039 char maskStr[INET_ADDRSTRLEN];
1040 inet_ntop(AF_INET, &netmask, maskStr, sizeof(maskStr));
1041 log<level::ERR>("Invalid netmask", entry("NETMASK=%s", maskStr));
1042 elog<InternalFailure>();
1043 }
Johnathan Mantey62c05dd2019-11-20 14:07:44 -08001044 return static_cast<bool>(x)
1045 ? AddrFamily<AF_INET>::defaultPrefix - __builtin_ctz(x)
1046 : 0;
William A. Kennington IIIc514d872019-04-06 18:19:38 -07001047}
1048
1049// We need to store this value so it can be returned to the client
1050// It is volatile so safe to store in daemon memory.
1051static std::unordered_map<uint8_t, SetStatus> setStatus;
1052
1053// Until we have good support for fixed versions of IPMI tool
1054// we need to return the VLAN id for disabled VLANs. The value is only
1055// used for verification that a disable operation succeeded and will only
1056// be sent if our system indicates that vlans are disabled.
1057static std::unordered_map<uint8_t, uint16_t> lastDisabledVlan;
1058
1059/** @brief Gets the set status for the channel if it exists
1060 * Otherise populates and returns the default value.
1061 *
1062 * @param[in] channel - The channel id corresponding to an ethernet interface
1063 * @return A reference to the SetStatus for the channel
1064 */
1065SetStatus& getSetStatus(uint8_t channel)
1066{
1067 auto it = setStatus.find(channel);
1068 if (it != setStatus.end())
1069 {
1070 return it->second;
1071 }
1072 return setStatus[channel] = SetStatus::Complete;
1073}
1074
Johnathan Manteyb87034e2019-09-16 10:50:50 -07001075/**
1076 * Define placeholder command handlers for the OEM Extension bytes for the Set
1077 * LAN Configuration Parameters and Get LAN Configuration Parameters
1078 * commands. Using "weak" linking allows the placeholder setLanOem/getLanOem
1079 * functions below to be overridden.
1080 * To create handlers for your own proprietary command set:
1081 * Create/modify a phosphor-ipmi-host Bitbake append file within your Yocto
1082 * recipe
1083 * Create C++ file(s) that define IPMI handler functions matching the
1084 * function names below (i.e. setLanOem). The default name for the
1085 * transport IPMI commands is transporthandler_oem.cpp.
1086 * Add:
1087 * EXTRA_OECONF_append = " --enable-transport-oem=yes"
1088 * Create a do_compile_prepend()/do_install_append method in your
1089 * bbappend file to copy the file to the build directory.
1090 * Add:
1091 * PROJECT_SRC_DIR := "${THISDIR}/${PN}"
1092 * # Copy the "strong" functions into the working directory, overriding the
1093 * # placeholder functions.
1094 * do_compile_prepend(){
1095 * cp -f ${PROJECT_SRC_DIR}/transporthandler_oem.cpp ${S}
1096 * }
1097 *
1098 * # Clean up after complilation has completed
1099 * do_install_append(){
1100 * rm -f ${S}/transporthandler_oem.cpp
1101 * }
1102 *
1103 */
1104
1105/**
1106 * Define the placeholder OEM commands as having weak linkage. Create
1107 * setLanOem, and getLanOem functions in the transporthandler_oem.cpp
1108 * file. The functions defined there must not have the "weak" attribute
1109 * applied to them.
1110 */
1111RspType<> setLanOem(uint8_t channel, uint8_t parameter, message::Payload& req)
1112 __attribute__((weak));
1113RspType<message::Payload> getLanOem(uint8_t channel, uint8_t parameter,
1114 uint8_t set, uint8_t block)
1115 __attribute__((weak));
1116
1117RspType<> setLanOem(uint8_t channel, uint8_t parameter, message::Payload& req)
1118{
1119 req.trailingOk = true;
1120 return response(ccParamNotSupported);
1121}
1122
1123RspType<message::Payload> getLanOem(uint8_t channel, uint8_t parameter,
1124 uint8_t set, uint8_t block)
1125{
1126 return response(ccParamNotSupported);
1127}
1128
William A. Kennington IIIc514d872019-04-06 18:19:38 -07001129RspType<> setLan(uint4_t channelBits, uint4_t, uint8_t parameter,
1130 message::Payload& req)
1131{
1132 auto channel = static_cast<uint8_t>(channelBits);
1133 if (!doesDeviceExist(channel))
1134 {
1135 req.trailingOk = true;
1136 return responseInvalidFieldRequest();
1137 }
1138
1139 switch (static_cast<LanParam>(parameter))
1140 {
1141 case LanParam::SetStatus:
1142 {
1143 uint2_t flag;
1144 uint6_t rsvd;
1145 if (req.unpack(flag, rsvd) != 0 || !req.fullyUnpacked())
1146 {
1147 return responseReqDataLenInvalid();
1148 }
1149 auto status = static_cast<SetStatus>(static_cast<uint8_t>(flag));
1150 switch (status)
1151 {
1152 case SetStatus::Complete:
1153 {
1154 getSetStatus(channel) = status;
1155 return responseSuccess();
1156 }
1157 case SetStatus::InProgress:
1158 {
1159 auto& storedStatus = getSetStatus(channel);
1160 if (storedStatus == SetStatus::InProgress)
1161 {
1162 return response(ccParamSetLocked);
1163 }
1164 storedStatus = status;
1165 return responseSuccess();
1166 }
1167 case SetStatus::Commit:
1168 if (getSetStatus(channel) != SetStatus::InProgress)
1169 {
1170 return responseInvalidFieldRequest();
1171 }
1172 return responseSuccess();
1173 }
1174 return response(ccParamNotSupported);
1175 }
1176 case LanParam::AuthSupport:
1177 {
1178 req.trailingOk = true;
1179 return response(ccParamReadOnly);
1180 }
1181 case LanParam::AuthEnables:
1182 {
1183 req.trailingOk = true;
Johnathan Mantey76ce9c72019-11-14 14:41:46 -08001184 return response(ccParamReadOnly);
William A. Kennington IIIc514d872019-04-06 18:19:38 -07001185 }
William A. Kennington IIIaab20232018-11-19 18:20:39 -08001186 case LanParam::IP:
Hariharasubramanian R83951912016-01-20 07:06:36 -06001187 {
William A. Kennington IIIc514d872019-04-06 18:19:38 -07001188 in_addr ip;
1189 std::array<uint8_t, sizeof(ip)> bytes;
1190 if (req.unpack(bytes) != 0 || !req.fullyUnpacked())
1191 {
1192 return responseReqDataLenInvalid();
1193 }
1194 copyInto(ip, bytes);
1195 channelCall<reconfigureIfAddr4>(channel, ip, std::nullopt);
1196 return responseSuccess();
Ratan Guptab8e99552017-07-27 07:07:48 +05301197 }
William A. Kennington IIIc514d872019-04-06 18:19:38 -07001198 case LanParam::IPSrc:
Ratan Guptacc6cdbf2017-09-01 23:06:25 +05301199 {
William A. Kennington IIIc514d872019-04-06 18:19:38 -07001200 uint4_t flag;
1201 uint4_t rsvd;
1202 if (req.unpack(flag, rsvd) != 0 || !req.fullyUnpacked())
1203 {
1204 return responseReqDataLenInvalid();
1205 }
1206 switch (static_cast<IPSrc>(static_cast<uint8_t>(flag)))
1207 {
1208 case IPSrc::DHCP:
1209 {
1210 channelCall<setDHCPProperty>(channel, true);
1211 return responseSuccess();
1212 }
1213 case IPSrc::Unspecified:
1214 case IPSrc::Static:
1215 case IPSrc::BIOS:
1216 case IPSrc::BMC:
1217 {
1218 channelCall<setDHCPProperty>(channel, false);
1219 return responseSuccess();
1220 }
1221 }
1222 return response(ccParamNotSupported);
Ratan Guptacc6cdbf2017-09-01 23:06:25 +05301223 }
William A. Kennington IIIaab20232018-11-19 18:20:39 -08001224 case LanParam::MAC:
Ratan Guptab8e99552017-07-27 07:07:48 +05301225 {
William A. Kennington IIIc514d872019-04-06 18:19:38 -07001226 ether_addr mac;
1227 std::array<uint8_t, sizeof(mac)> bytes;
1228 if (req.unpack(bytes) != 0 || !req.fullyUnpacked())
Suryakanth Sekar0a327e12019-08-08 14:30:19 +05301229 {
William A. Kennington IIIc514d872019-04-06 18:19:38 -07001230 return responseReqDataLenInvalid();
Suryakanth Sekar0a327e12019-08-08 14:30:19 +05301231 }
William A. Kennington IIIc514d872019-04-06 18:19:38 -07001232 copyInto(mac, bytes);
1233 channelCall<setMACProperty>(channel, mac);
1234 return responseSuccess();
Ratan Gupta533d03b2017-07-30 10:39:22 +05301235 }
William A. Kennington IIIc514d872019-04-06 18:19:38 -07001236 case LanParam::SubnetMask:
Ratan Guptab8e99552017-07-27 07:07:48 +05301237 {
William A. Kennington IIIc514d872019-04-06 18:19:38 -07001238 in_addr netmask;
1239 std::array<uint8_t, sizeof(netmask)> bytes;
1240 if (req.unpack(bytes) != 0 || !req.fullyUnpacked())
Ratan Guptab8e99552017-07-27 07:07:48 +05301241 {
William A. Kennington IIIc514d872019-04-06 18:19:38 -07001242 return responseReqDataLenInvalid();
Ratan Guptab8e99552017-07-27 07:07:48 +05301243 }
William A. Kennington IIIc514d872019-04-06 18:19:38 -07001244 copyInto(netmask, bytes);
1245 channelCall<reconfigureIfAddr4>(channel, std::nullopt,
1246 netmaskToPrefix(netmask));
1247 return responseSuccess();
Ratan Guptab8e99552017-07-27 07:07:48 +05301248 }
William A. Kennington IIIc514d872019-04-06 18:19:38 -07001249 case LanParam::Gateway1:
Ratan Guptab8e99552017-07-27 07:07:48 +05301250 {
William A. Kennington IIIc514d872019-04-06 18:19:38 -07001251 in_addr gateway;
1252 std::array<uint8_t, sizeof(gateway)> bytes;
1253 if (req.unpack(bytes) != 0 || !req.fullyUnpacked())
1254 {
1255 return responseReqDataLenInvalid();
1256 }
1257 copyInto(gateway, bytes);
1258 channelCall<setGatewayProperty<AF_INET>>(channel, gateway);
1259 return responseSuccess();
1260 }
William A. Kennington III4bbc3db2019-04-15 00:02:10 -07001261 case LanParam::Gateway1MAC:
1262 {
1263 ether_addr gatewayMAC;
1264 std::array<uint8_t, sizeof(gatewayMAC)> bytes;
1265 if (req.unpack(bytes) != 0 || !req.fullyUnpacked())
1266 {
1267 return responseReqDataLenInvalid();
1268 }
1269 copyInto(gatewayMAC, bytes);
1270 channelCall<reconfigureGatewayMAC<AF_INET>>(channel, gatewayMAC);
1271 return responseSuccess();
1272 }
William A. Kennington IIIc514d872019-04-06 18:19:38 -07001273 case LanParam::VLANId:
1274 {
1275 uint16_t vlanData;
1276 if (req.unpack(vlanData) != 0 || !req.fullyUnpacked())
1277 {
1278 return responseReqDataLenInvalid();
1279 }
1280 if ((vlanData & VLAN_ENABLE_FLAG) == 0)
1281 {
1282 lastDisabledVlan[channel] = vlanData & VLAN_VALUE_MASK;
1283 vlanData = 0;
1284 }
1285 channelCall<reconfigureVLAN>(channel, vlanData & VLAN_VALUE_MASK);
1286 return responseSuccess();
1287 }
1288 case LanParam::CiphersuiteSupport:
1289 case LanParam::CiphersuiteEntries:
1290 {
1291 req.trailingOk = true;
1292 return response(ccParamReadOnly);
Ratan Guptab8e99552017-07-27 07:07:48 +05301293 }
Ratan Guptab8e99552017-07-27 07:07:48 +05301294 }
vishwa1eaea4f2016-02-26 11:57:40 -06001295
Johnathan Manteyb87034e2019-09-16 10:50:50 -07001296 if ((parameter >= oemCmdStart) && (parameter <= oemCmdEnd))
1297 {
1298 return setLanOem(channel, parameter, req);
1299 }
1300
William A. Kennington IIIc514d872019-04-06 18:19:38 -07001301 req.trailingOk = true;
1302 return response(ccParamNotSupported);
Adriana Kobylak5d6481f2015-10-29 21:44:55 -05001303}
1304
William A. Kennington IIIc514d872019-04-06 18:19:38 -07001305RspType<message::Payload> getLan(uint4_t channelBits, uint3_t, bool revOnly,
1306 uint8_t parameter, uint8_t set, uint8_t block)
Ratan Guptab8e99552017-07-27 07:07:48 +05301307{
William A. Kennington IIIc514d872019-04-06 18:19:38 -07001308 message::Payload ret;
1309 constexpr uint8_t current_revision = 0x11;
1310 ret.pack(current_revision);
Adriana Kobylak5d6481f2015-10-29 21:44:55 -05001311
William A. Kennington IIIc514d872019-04-06 18:19:38 -07001312 if (revOnly)
Suryakanth Sekare4054402019-08-08 15:16:52 +05301313 {
William A. Kennington IIIc514d872019-04-06 18:19:38 -07001314 return responseSuccess(std::move(ret));
Suryakanth Sekare4054402019-08-08 15:16:52 +05301315 }
Adriana Kobylak5d6481f2015-10-29 21:44:55 -05001316
William A. Kennington IIIc514d872019-04-06 18:19:38 -07001317 auto channel = static_cast<uint8_t>(channelBits);
1318 if (!doesDeviceExist(channel))
Adriana Kobylak5d6481f2015-10-29 21:44:55 -05001319 {
William A. Kennington IIIc514d872019-04-06 18:19:38 -07001320 return responseInvalidFieldRequest();
Adriana Kobylak5d6481f2015-10-29 21:44:55 -05001321 }
1322
Johnathan Manteyaffadb52019-10-07 10:13:53 -07001323 static std::vector<uint8_t> cipherList;
1324 static bool listInit = false;
1325 if (!listInit)
1326 {
1327 try
1328 {
1329 cipherList = cipher::getCipherList();
1330 listInit = true;
1331 }
1332 catch (const std::exception& e)
1333 {
1334 }
1335 }
1336
William A. Kennington IIIc514d872019-04-06 18:19:38 -07001337 switch (static_cast<LanParam>(parameter))
Tom Josepha30c8d32018-03-22 02:15:03 +05301338 {
William A. Kennington IIIc514d872019-04-06 18:19:38 -07001339 case LanParam::SetStatus:
Tom Josepha30c8d32018-03-22 02:15:03 +05301340 {
William A. Kennington IIIc514d872019-04-06 18:19:38 -07001341 SetStatus status;
1342 try
1343 {
1344 status = setStatus.at(channel);
1345 }
1346 catch (const std::out_of_range&)
1347 {
1348 status = SetStatus::Complete;
1349 }
1350 ret.pack(static_cast<uint2_t>(status), uint6_t{});
1351 return responseSuccess(std::move(ret));
Tom Josepha30c8d32018-03-22 02:15:03 +05301352 }
William A. Kennington IIIc514d872019-04-06 18:19:38 -07001353 case LanParam::AuthSupport:
Tom Josepha30c8d32018-03-22 02:15:03 +05301354 {
William A. Kennington IIIc514d872019-04-06 18:19:38 -07001355 std::bitset<6> support;
1356 ret.pack(support, uint2_t{});
1357 return responseSuccess(std::move(ret));
Tom Josepha30c8d32018-03-22 02:15:03 +05301358 }
William A. Kennington IIIc514d872019-04-06 18:19:38 -07001359 case LanParam::AuthEnables:
vishwa1eaea4f2016-02-26 11:57:40 -06001360 {
William A. Kennington IIIc514d872019-04-06 18:19:38 -07001361 std::bitset<6> enables;
1362 ret.pack(enables, uint2_t{}); // Callback
1363 ret.pack(enables, uint2_t{}); // User
1364 ret.pack(enables, uint2_t{}); // Operator
1365 ret.pack(enables, uint2_t{}); // Admin
1366 ret.pack(enables, uint2_t{}); // OEM
1367 return responseSuccess(std::move(ret));
William A. Kennington III39f94ef2018-11-19 22:36:16 -08001368 }
William A. Kennington IIIaab20232018-11-19 18:20:39 -08001369 case LanParam::IP:
William A. Kennington IIIc514d872019-04-06 18:19:38 -07001370 {
1371 auto ifaddr = channelCall<getIfAddr4>(channel);
1372 in_addr addr{};
1373 if (ifaddr)
1374 {
1375 addr = ifaddr->address;
1376 }
1377 ret.pack(dataRef(addr));
1378 return responseSuccess(std::move(ret));
1379 }
1380 case LanParam::IPSrc:
1381 {
1382 auto src = IPSrc::Static;
1383 if (channelCall<getDHCPProperty>(channel))
1384 {
1385 src = IPSrc::DHCP;
1386 }
1387 ret.pack(static_cast<uint4_t>(src), uint4_t{});
1388 return responseSuccess(std::move(ret));
1389 }
William A. Kennington IIIaab20232018-11-19 18:20:39 -08001390 case LanParam::MAC:
William A. Kennington III39f94ef2018-11-19 22:36:16 -08001391 {
William A. Kennington IIIc514d872019-04-06 18:19:38 -07001392 ether_addr mac = channelCall<getMACProperty>(channel);
1393 ret.pack(dataRef(mac));
1394 return responseSuccess(std::move(ret));
1395 }
1396 case LanParam::SubnetMask:
1397 {
1398 auto ifaddr = channelCall<getIfAddr4>(channel);
1399 uint8_t prefix = AddrFamily<AF_INET>::defaultPrefix;
1400 if (ifaddr)
Ratan Guptab8e99552017-07-27 07:07:48 +05301401 {
William A. Kennington IIIc514d872019-04-06 18:19:38 -07001402 prefix = ifaddr->prefix;
1403 }
1404 in_addr netmask = prefixToNetmask(prefix);
1405 ret.pack(dataRef(netmask));
1406 return responseSuccess(std::move(ret));
1407 }
1408 case LanParam::Gateway1:
1409 {
1410 auto gateway =
1411 channelCall<getGatewayProperty<AF_INET>>(channel).value_or(
1412 in_addr{});
1413 ret.pack(dataRef(gateway));
1414 return responseSuccess(std::move(ret));
1415 }
William A. Kennington III4bbc3db2019-04-15 00:02:10 -07001416 case LanParam::Gateway1MAC:
1417 {
1418 ether_addr mac{};
1419 auto neighbor = channelCall<getGatewayNeighbor<AF_INET>>(channel);
1420 if (neighbor)
1421 {
1422 mac = neighbor->mac;
1423 }
1424 ret.pack(dataRef(mac));
1425 return responseSuccess(std::move(ret));
1426 }
William A. Kennington IIIc514d872019-04-06 18:19:38 -07001427 case LanParam::VLANId:
1428 {
1429 uint16_t vlan = channelCall<getVLANProperty>(channel);
1430 if (vlan != 0)
1431 {
1432 vlan |= VLAN_ENABLE_FLAG;
Ratan Guptab8e99552017-07-27 07:07:48 +05301433 }
1434 else
1435 {
William A. Kennington IIIc514d872019-04-06 18:19:38 -07001436 vlan = lastDisabledVlan[channel];
Ratan Guptab8e99552017-07-27 07:07:48 +05301437 }
William A. Kennington IIIc514d872019-04-06 18:19:38 -07001438 ret.pack(vlan);
1439 return responseSuccess(std::move(ret));
Adriana Kobylak342df102016-02-10 13:48:16 -06001440 }
William A. Kennington IIIc514d872019-04-06 18:19:38 -07001441 case LanParam::CiphersuiteSupport:
Johnathan Manteyaffadb52019-10-07 10:13:53 -07001442 {
1443 if (!listInit)
1444 {
1445 return responseUnspecifiedError();
1446 }
1447 ret.pack(static_cast<uint8_t>(cipherList.size() - 1));
1448 return responseSuccess(std::move(ret));
1449 }
William A. Kennington IIIc514d872019-04-06 18:19:38 -07001450 case LanParam::CiphersuiteEntries:
Johnathan Manteyaffadb52019-10-07 10:13:53 -07001451 {
1452 if (!listInit)
1453 {
1454 return responseUnspecifiedError();
1455 }
1456 ret.pack(cipherList);
1457 return responseSuccess(std::move(ret));
1458 }
Adriana Kobylak5d6481f2015-10-29 21:44:55 -05001459 }
1460
Johnathan Manteyb87034e2019-09-16 10:50:50 -07001461 if ((parameter >= oemCmdStart) && (parameter <= oemCmdEnd))
1462 {
1463 return getLanOem(channel, parameter, set, block);
1464 }
1465
William A. Kennington IIIc514d872019-04-06 18:19:38 -07001466 return response(ccParamNotSupported);
Adriana Kobylak5d6481f2015-10-29 21:44:55 -05001467}
1468
William A. Kennington IIIc514d872019-04-06 18:19:38 -07001469} // namespace transport
1470} // namespace ipmi
Ratan Gupta1247e0b2018-03-07 10:47:25 +05301471
William A. Kennington IIIc514d872019-04-06 18:19:38 -07001472void register_netfn_transport_functions() __attribute__((constructor));
Ratan Gupta1247e0b2018-03-07 10:47:25 +05301473
Adriana Kobylak5d6481f2015-10-29 21:44:55 -05001474void register_netfn_transport_functions()
1475{
William A. Kennington IIIc514d872019-04-06 18:19:38 -07001476 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnTransport,
1477 ipmi::transport::cmdSetLanConfigParameters,
1478 ipmi::Privilege::Admin, ipmi::transport::setLan);
1479 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnTransport,
1480 ipmi::transport::cmdGetLanConfigParameters,
Johnathan Mantey34698d52019-11-19 14:47:30 -08001481 ipmi::Privilege::Operator, ipmi::transport::getLan);
Adriana Kobylak5d6481f2015-10-29 21:44:55 -05001482}