blob: a88e0a21a9cbe1d0c1204e3a14596f6b2f606867 [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 }
1044 return 32 - __builtin_ctz(x);
1045}
1046
1047// We need to store this value so it can be returned to the client
1048// It is volatile so safe to store in daemon memory.
1049static std::unordered_map<uint8_t, SetStatus> setStatus;
1050
1051// Until we have good support for fixed versions of IPMI tool
1052// we need to return the VLAN id for disabled VLANs. The value is only
1053// used for verification that a disable operation succeeded and will only
1054// be sent if our system indicates that vlans are disabled.
1055static std::unordered_map<uint8_t, uint16_t> lastDisabledVlan;
1056
1057/** @brief Gets the set status for the channel if it exists
1058 * Otherise populates and returns the default value.
1059 *
1060 * @param[in] channel - The channel id corresponding to an ethernet interface
1061 * @return A reference to the SetStatus for the channel
1062 */
1063SetStatus& getSetStatus(uint8_t channel)
1064{
1065 auto it = setStatus.find(channel);
1066 if (it != setStatus.end())
1067 {
1068 return it->second;
1069 }
1070 return setStatus[channel] = SetStatus::Complete;
1071}
1072
Johnathan Manteyb87034e2019-09-16 10:50:50 -07001073/**
1074 * Define placeholder command handlers for the OEM Extension bytes for the Set
1075 * LAN Configuration Parameters and Get LAN Configuration Parameters
1076 * commands. Using "weak" linking allows the placeholder setLanOem/getLanOem
1077 * functions below to be overridden.
1078 * To create handlers for your own proprietary command set:
1079 * Create/modify a phosphor-ipmi-host Bitbake append file within your Yocto
1080 * recipe
1081 * Create C++ file(s) that define IPMI handler functions matching the
1082 * function names below (i.e. setLanOem). The default name for the
1083 * transport IPMI commands is transporthandler_oem.cpp.
1084 * Add:
1085 * EXTRA_OECONF_append = " --enable-transport-oem=yes"
1086 * Create a do_compile_prepend()/do_install_append method in your
1087 * bbappend file to copy the file to the build directory.
1088 * Add:
1089 * PROJECT_SRC_DIR := "${THISDIR}/${PN}"
1090 * # Copy the "strong" functions into the working directory, overriding the
1091 * # placeholder functions.
1092 * do_compile_prepend(){
1093 * cp -f ${PROJECT_SRC_DIR}/transporthandler_oem.cpp ${S}
1094 * }
1095 *
1096 * # Clean up after complilation has completed
1097 * do_install_append(){
1098 * rm -f ${S}/transporthandler_oem.cpp
1099 * }
1100 *
1101 */
1102
1103/**
1104 * Define the placeholder OEM commands as having weak linkage. Create
1105 * setLanOem, and getLanOem functions in the transporthandler_oem.cpp
1106 * file. The functions defined there must not have the "weak" attribute
1107 * applied to them.
1108 */
1109RspType<> setLanOem(uint8_t channel, uint8_t parameter, message::Payload& req)
1110 __attribute__((weak));
1111RspType<message::Payload> getLanOem(uint8_t channel, uint8_t parameter,
1112 uint8_t set, uint8_t block)
1113 __attribute__((weak));
1114
1115RspType<> setLanOem(uint8_t channel, uint8_t parameter, message::Payload& req)
1116{
1117 req.trailingOk = true;
1118 return response(ccParamNotSupported);
1119}
1120
1121RspType<message::Payload> getLanOem(uint8_t channel, uint8_t parameter,
1122 uint8_t set, uint8_t block)
1123{
1124 return response(ccParamNotSupported);
1125}
1126
William A. Kennington IIIc514d872019-04-06 18:19:38 -07001127RspType<> setLan(uint4_t channelBits, uint4_t, uint8_t parameter,
1128 message::Payload& req)
1129{
1130 auto channel = static_cast<uint8_t>(channelBits);
1131 if (!doesDeviceExist(channel))
1132 {
1133 req.trailingOk = true;
1134 return responseInvalidFieldRequest();
1135 }
1136
1137 switch (static_cast<LanParam>(parameter))
1138 {
1139 case LanParam::SetStatus:
1140 {
1141 uint2_t flag;
1142 uint6_t rsvd;
1143 if (req.unpack(flag, rsvd) != 0 || !req.fullyUnpacked())
1144 {
1145 return responseReqDataLenInvalid();
1146 }
1147 auto status = static_cast<SetStatus>(static_cast<uint8_t>(flag));
1148 switch (status)
1149 {
1150 case SetStatus::Complete:
1151 {
1152 getSetStatus(channel) = status;
1153 return responseSuccess();
1154 }
1155 case SetStatus::InProgress:
1156 {
1157 auto& storedStatus = getSetStatus(channel);
1158 if (storedStatus == SetStatus::InProgress)
1159 {
1160 return response(ccParamSetLocked);
1161 }
1162 storedStatus = status;
1163 return responseSuccess();
1164 }
1165 case SetStatus::Commit:
1166 if (getSetStatus(channel) != SetStatus::InProgress)
1167 {
1168 return responseInvalidFieldRequest();
1169 }
1170 return responseSuccess();
1171 }
1172 return response(ccParamNotSupported);
1173 }
1174 case LanParam::AuthSupport:
1175 {
1176 req.trailingOk = true;
1177 return response(ccParamReadOnly);
1178 }
1179 case LanParam::AuthEnables:
1180 {
1181 req.trailingOk = true;
1182 return response(ccParamNotSupported);
1183 }
William A. Kennington IIIaab20232018-11-19 18:20:39 -08001184 case LanParam::IP:
Hariharasubramanian R83951912016-01-20 07:06:36 -06001185 {
William A. Kennington IIIc514d872019-04-06 18:19:38 -07001186 in_addr ip;
1187 std::array<uint8_t, sizeof(ip)> bytes;
1188 if (req.unpack(bytes) != 0 || !req.fullyUnpacked())
1189 {
1190 return responseReqDataLenInvalid();
1191 }
1192 copyInto(ip, bytes);
1193 channelCall<reconfigureIfAddr4>(channel, ip, std::nullopt);
1194 return responseSuccess();
Ratan Guptab8e99552017-07-27 07:07:48 +05301195 }
William A. Kennington IIIc514d872019-04-06 18:19:38 -07001196 case LanParam::IPSrc:
Ratan Guptacc6cdbf2017-09-01 23:06:25 +05301197 {
William A. Kennington IIIc514d872019-04-06 18:19:38 -07001198 uint4_t flag;
1199 uint4_t rsvd;
1200 if (req.unpack(flag, rsvd) != 0 || !req.fullyUnpacked())
1201 {
1202 return responseReqDataLenInvalid();
1203 }
1204 switch (static_cast<IPSrc>(static_cast<uint8_t>(flag)))
1205 {
1206 case IPSrc::DHCP:
1207 {
1208 channelCall<setDHCPProperty>(channel, true);
1209 return responseSuccess();
1210 }
1211 case IPSrc::Unspecified:
1212 case IPSrc::Static:
1213 case IPSrc::BIOS:
1214 case IPSrc::BMC:
1215 {
1216 channelCall<setDHCPProperty>(channel, false);
1217 return responseSuccess();
1218 }
1219 }
1220 return response(ccParamNotSupported);
Ratan Guptacc6cdbf2017-09-01 23:06:25 +05301221 }
William A. Kennington IIIaab20232018-11-19 18:20:39 -08001222 case LanParam::MAC:
Ratan Guptab8e99552017-07-27 07:07:48 +05301223 {
William A. Kennington IIIc514d872019-04-06 18:19:38 -07001224 ether_addr mac;
1225 std::array<uint8_t, sizeof(mac)> bytes;
1226 if (req.unpack(bytes) != 0 || !req.fullyUnpacked())
Suryakanth Sekar0a327e12019-08-08 14:30:19 +05301227 {
William A. Kennington IIIc514d872019-04-06 18:19:38 -07001228 return responseReqDataLenInvalid();
Suryakanth Sekar0a327e12019-08-08 14:30:19 +05301229 }
William A. Kennington IIIc514d872019-04-06 18:19:38 -07001230 copyInto(mac, bytes);
1231 channelCall<setMACProperty>(channel, mac);
1232 return responseSuccess();
Ratan Gupta533d03b2017-07-30 10:39:22 +05301233 }
William A. Kennington IIIc514d872019-04-06 18:19:38 -07001234 case LanParam::SubnetMask:
Ratan Guptab8e99552017-07-27 07:07:48 +05301235 {
William A. Kennington IIIc514d872019-04-06 18:19:38 -07001236 in_addr netmask;
1237 std::array<uint8_t, sizeof(netmask)> bytes;
1238 if (req.unpack(bytes) != 0 || !req.fullyUnpacked())
Ratan Guptab8e99552017-07-27 07:07:48 +05301239 {
William A. Kennington IIIc514d872019-04-06 18:19:38 -07001240 return responseReqDataLenInvalid();
Ratan Guptab8e99552017-07-27 07:07:48 +05301241 }
William A. Kennington IIIc514d872019-04-06 18:19:38 -07001242 copyInto(netmask, bytes);
1243 channelCall<reconfigureIfAddr4>(channel, std::nullopt,
1244 netmaskToPrefix(netmask));
1245 return responseSuccess();
Ratan Guptab8e99552017-07-27 07:07:48 +05301246 }
William A. Kennington IIIc514d872019-04-06 18:19:38 -07001247 case LanParam::Gateway1:
Ratan Guptab8e99552017-07-27 07:07:48 +05301248 {
William A. Kennington IIIc514d872019-04-06 18:19:38 -07001249 in_addr gateway;
1250 std::array<uint8_t, sizeof(gateway)> bytes;
1251 if (req.unpack(bytes) != 0 || !req.fullyUnpacked())
1252 {
1253 return responseReqDataLenInvalid();
1254 }
1255 copyInto(gateway, bytes);
1256 channelCall<setGatewayProperty<AF_INET>>(channel, gateway);
1257 return responseSuccess();
1258 }
William A. Kennington III4bbc3db2019-04-15 00:02:10 -07001259 case LanParam::Gateway1MAC:
1260 {
1261 ether_addr gatewayMAC;
1262 std::array<uint8_t, sizeof(gatewayMAC)> bytes;
1263 if (req.unpack(bytes) != 0 || !req.fullyUnpacked())
1264 {
1265 return responseReqDataLenInvalid();
1266 }
1267 copyInto(gatewayMAC, bytes);
1268 channelCall<reconfigureGatewayMAC<AF_INET>>(channel, gatewayMAC);
1269 return responseSuccess();
1270 }
William A. Kennington IIIc514d872019-04-06 18:19:38 -07001271 case LanParam::VLANId:
1272 {
1273 uint16_t vlanData;
1274 if (req.unpack(vlanData) != 0 || !req.fullyUnpacked())
1275 {
1276 return responseReqDataLenInvalid();
1277 }
1278 if ((vlanData & VLAN_ENABLE_FLAG) == 0)
1279 {
1280 lastDisabledVlan[channel] = vlanData & VLAN_VALUE_MASK;
1281 vlanData = 0;
1282 }
1283 channelCall<reconfigureVLAN>(channel, vlanData & VLAN_VALUE_MASK);
1284 return responseSuccess();
1285 }
1286 case LanParam::CiphersuiteSupport:
1287 case LanParam::CiphersuiteEntries:
1288 {
1289 req.trailingOk = true;
1290 return response(ccParamReadOnly);
Ratan Guptab8e99552017-07-27 07:07:48 +05301291 }
Ratan Guptab8e99552017-07-27 07:07:48 +05301292 }
vishwa1eaea4f2016-02-26 11:57:40 -06001293
Johnathan Manteyb87034e2019-09-16 10:50:50 -07001294 if ((parameter >= oemCmdStart) && (parameter <= oemCmdEnd))
1295 {
1296 return setLanOem(channel, parameter, req);
1297 }
1298
William A. Kennington IIIc514d872019-04-06 18:19:38 -07001299 req.trailingOk = true;
1300 return response(ccParamNotSupported);
Adriana Kobylak5d6481f2015-10-29 21:44:55 -05001301}
1302
William A. Kennington IIIc514d872019-04-06 18:19:38 -07001303RspType<message::Payload> getLan(uint4_t channelBits, uint3_t, bool revOnly,
1304 uint8_t parameter, uint8_t set, uint8_t block)
Ratan Guptab8e99552017-07-27 07:07:48 +05301305{
William A. Kennington IIIc514d872019-04-06 18:19:38 -07001306 message::Payload ret;
1307 constexpr uint8_t current_revision = 0x11;
1308 ret.pack(current_revision);
Adriana Kobylak5d6481f2015-10-29 21:44:55 -05001309
William A. Kennington IIIc514d872019-04-06 18:19:38 -07001310 if (revOnly)
Suryakanth Sekare4054402019-08-08 15:16:52 +05301311 {
William A. Kennington IIIc514d872019-04-06 18:19:38 -07001312 return responseSuccess(std::move(ret));
Suryakanth Sekare4054402019-08-08 15:16:52 +05301313 }
Adriana Kobylak5d6481f2015-10-29 21:44:55 -05001314
William A. Kennington IIIc514d872019-04-06 18:19:38 -07001315 auto channel = static_cast<uint8_t>(channelBits);
1316 if (!doesDeviceExist(channel))
Adriana Kobylak5d6481f2015-10-29 21:44:55 -05001317 {
William A. Kennington IIIc514d872019-04-06 18:19:38 -07001318 return responseInvalidFieldRequest();
Adriana Kobylak5d6481f2015-10-29 21:44:55 -05001319 }
1320
Johnathan Manteyaffadb52019-10-07 10:13:53 -07001321 static std::vector<uint8_t> cipherList;
1322 static bool listInit = false;
1323 if (!listInit)
1324 {
1325 try
1326 {
1327 cipherList = cipher::getCipherList();
1328 listInit = true;
1329 }
1330 catch (const std::exception& e)
1331 {
1332 }
1333 }
1334
William A. Kennington IIIc514d872019-04-06 18:19:38 -07001335 switch (static_cast<LanParam>(parameter))
Tom Josepha30c8d32018-03-22 02:15:03 +05301336 {
William A. Kennington IIIc514d872019-04-06 18:19:38 -07001337 case LanParam::SetStatus:
Tom Josepha30c8d32018-03-22 02:15:03 +05301338 {
William A. Kennington IIIc514d872019-04-06 18:19:38 -07001339 SetStatus status;
1340 try
1341 {
1342 status = setStatus.at(channel);
1343 }
1344 catch (const std::out_of_range&)
1345 {
1346 status = SetStatus::Complete;
1347 }
1348 ret.pack(static_cast<uint2_t>(status), uint6_t{});
1349 return responseSuccess(std::move(ret));
Tom Josepha30c8d32018-03-22 02:15:03 +05301350 }
William A. Kennington IIIc514d872019-04-06 18:19:38 -07001351 case LanParam::AuthSupport:
Tom Josepha30c8d32018-03-22 02:15:03 +05301352 {
William A. Kennington IIIc514d872019-04-06 18:19:38 -07001353 std::bitset<6> support;
1354 ret.pack(support, uint2_t{});
1355 return responseSuccess(std::move(ret));
Tom Josepha30c8d32018-03-22 02:15:03 +05301356 }
William A. Kennington IIIc514d872019-04-06 18:19:38 -07001357 case LanParam::AuthEnables:
vishwa1eaea4f2016-02-26 11:57:40 -06001358 {
William A. Kennington IIIc514d872019-04-06 18:19:38 -07001359 std::bitset<6> enables;
1360 ret.pack(enables, uint2_t{}); // Callback
1361 ret.pack(enables, uint2_t{}); // User
1362 ret.pack(enables, uint2_t{}); // Operator
1363 ret.pack(enables, uint2_t{}); // Admin
1364 ret.pack(enables, uint2_t{}); // OEM
1365 return responseSuccess(std::move(ret));
William A. Kennington III39f94ef2018-11-19 22:36:16 -08001366 }
William A. Kennington IIIaab20232018-11-19 18:20:39 -08001367 case LanParam::IP:
William A. Kennington IIIc514d872019-04-06 18:19:38 -07001368 {
1369 auto ifaddr = channelCall<getIfAddr4>(channel);
1370 in_addr addr{};
1371 if (ifaddr)
1372 {
1373 addr = ifaddr->address;
1374 }
1375 ret.pack(dataRef(addr));
1376 return responseSuccess(std::move(ret));
1377 }
1378 case LanParam::IPSrc:
1379 {
1380 auto src = IPSrc::Static;
1381 if (channelCall<getDHCPProperty>(channel))
1382 {
1383 src = IPSrc::DHCP;
1384 }
1385 ret.pack(static_cast<uint4_t>(src), uint4_t{});
1386 return responseSuccess(std::move(ret));
1387 }
William A. Kennington IIIaab20232018-11-19 18:20:39 -08001388 case LanParam::MAC:
William A. Kennington III39f94ef2018-11-19 22:36:16 -08001389 {
William A. Kennington IIIc514d872019-04-06 18:19:38 -07001390 ether_addr mac = channelCall<getMACProperty>(channel);
1391 ret.pack(dataRef(mac));
1392 return responseSuccess(std::move(ret));
1393 }
1394 case LanParam::SubnetMask:
1395 {
1396 auto ifaddr = channelCall<getIfAddr4>(channel);
1397 uint8_t prefix = AddrFamily<AF_INET>::defaultPrefix;
1398 if (ifaddr)
Ratan Guptab8e99552017-07-27 07:07:48 +05301399 {
William A. Kennington IIIc514d872019-04-06 18:19:38 -07001400 prefix = ifaddr->prefix;
1401 }
1402 in_addr netmask = prefixToNetmask(prefix);
1403 ret.pack(dataRef(netmask));
1404 return responseSuccess(std::move(ret));
1405 }
1406 case LanParam::Gateway1:
1407 {
1408 auto gateway =
1409 channelCall<getGatewayProperty<AF_INET>>(channel).value_or(
1410 in_addr{});
1411 ret.pack(dataRef(gateway));
1412 return responseSuccess(std::move(ret));
1413 }
William A. Kennington III4bbc3db2019-04-15 00:02:10 -07001414 case LanParam::Gateway1MAC:
1415 {
1416 ether_addr mac{};
1417 auto neighbor = channelCall<getGatewayNeighbor<AF_INET>>(channel);
1418 if (neighbor)
1419 {
1420 mac = neighbor->mac;
1421 }
1422 ret.pack(dataRef(mac));
1423 return responseSuccess(std::move(ret));
1424 }
William A. Kennington IIIc514d872019-04-06 18:19:38 -07001425 case LanParam::VLANId:
1426 {
1427 uint16_t vlan = channelCall<getVLANProperty>(channel);
1428 if (vlan != 0)
1429 {
1430 vlan |= VLAN_ENABLE_FLAG;
Ratan Guptab8e99552017-07-27 07:07:48 +05301431 }
1432 else
1433 {
William A. Kennington IIIc514d872019-04-06 18:19:38 -07001434 vlan = lastDisabledVlan[channel];
Ratan Guptab8e99552017-07-27 07:07:48 +05301435 }
William A. Kennington IIIc514d872019-04-06 18:19:38 -07001436 ret.pack(vlan);
1437 return responseSuccess(std::move(ret));
Adriana Kobylak342df102016-02-10 13:48:16 -06001438 }
William A. Kennington IIIc514d872019-04-06 18:19:38 -07001439 case LanParam::CiphersuiteSupport:
Johnathan Manteyaffadb52019-10-07 10:13:53 -07001440 {
1441 if (!listInit)
1442 {
1443 return responseUnspecifiedError();
1444 }
1445 ret.pack(static_cast<uint8_t>(cipherList.size() - 1));
1446 return responseSuccess(std::move(ret));
1447 }
William A. Kennington IIIc514d872019-04-06 18:19:38 -07001448 case LanParam::CiphersuiteEntries:
Johnathan Manteyaffadb52019-10-07 10:13:53 -07001449 {
1450 if (!listInit)
1451 {
1452 return responseUnspecifiedError();
1453 }
1454 ret.pack(cipherList);
1455 return responseSuccess(std::move(ret));
1456 }
Adriana Kobylak5d6481f2015-10-29 21:44:55 -05001457 }
1458
Johnathan Manteyb87034e2019-09-16 10:50:50 -07001459 if ((parameter >= oemCmdStart) && (parameter <= oemCmdEnd))
1460 {
1461 return getLanOem(channel, parameter, set, block);
1462 }
1463
William A. Kennington IIIc514d872019-04-06 18:19:38 -07001464 return response(ccParamNotSupported);
Adriana Kobylak5d6481f2015-10-29 21:44:55 -05001465}
1466
William A. Kennington IIIc514d872019-04-06 18:19:38 -07001467} // namespace transport
1468} // namespace ipmi
Ratan Gupta1247e0b2018-03-07 10:47:25 +05301469
William A. Kennington IIIc514d872019-04-06 18:19:38 -07001470void register_netfn_transport_functions() __attribute__((constructor));
Ratan Gupta1247e0b2018-03-07 10:47:25 +05301471
Adriana Kobylak5d6481f2015-10-29 21:44:55 -05001472void register_netfn_transport_functions()
1473{
William A. Kennington IIIc514d872019-04-06 18:19:38 -07001474 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnTransport,
1475 ipmi::transport::cmdSetLanConfigParameters,
1476 ipmi::Privilege::Admin, ipmi::transport::setLan);
1477 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnTransport,
1478 ipmi::transport::cmdGetLanConfigParameters,
1479 ipmi::Privilege::Admin, ipmi::transport::getLan);
Adriana Kobylak5d6481f2015-10-29 21:44:55 -05001480}