1aaeb29e3STom Joseph #pragma once 2d92bc324SVernon Mauery #include <netinet/in.h> 3d92bc324SVernon Mauery #include <sys/socket.h> 4d92bc324SVernon Mauery #include <sys/types.h> 5aaeb29e3STom Joseph 67a0142c5SVernon Mauery #include <boost/asio/ip/udp.hpp> 77b7f25f7SGeorge Liu #include <phosphor-logging/lg2.hpp> 8bc8958feSGeorge Liu 97a0142c5SVernon Mauery #include <memory> 10d92bc324SVernon Mauery #include <optional> 11aaeb29e3STom Joseph #include <string> 12aaeb29e3STom Joseph #include <tuple> 13d92bc324SVernon Mauery #include <variant> 14aaeb29e3STom Joseph #include <vector> 15aaeb29e3STom Joseph 16aaeb29e3STom Joseph namespace udpsocket 17aaeb29e3STom Joseph { 189979e997SRajashekar Gade Reddy static constexpr uint8_t v4v6Index = 12; 19aaeb29e3STom Joseph 20aaeb29e3STom Joseph /** @class Channel 21aaeb29e3STom Joseph * 22aaeb29e3STom Joseph * @brief Provides encapsulation for UDP socket operations like Read, Peek, 23aaeb29e3STom Joseph * Write, Remote peer's IP Address and Port. 24aaeb29e3STom Joseph */ 25aaeb29e3STom Joseph class Channel 26aaeb29e3STom Joseph { 27aaeb29e3STom Joseph public: 287a0142c5SVernon Mauery Channel() = delete; 297a0142c5SVernon Mauery ~Channel() = default; 307a0142c5SVernon Mauery Channel(const Channel& right) = delete; 317a0142c5SVernon Mauery Channel& operator=(const Channel& right) = delete; 327a0142c5SVernon Mauery Channel(Channel&&) = delete; 337a0142c5SVernon Mauery Channel& operator=(Channel&&) = delete; 34aaeb29e3STom Joseph 35aaeb29e3STom Joseph /** 36aaeb29e3STom Joseph * @brief Constructor 37aaeb29e3STom Joseph * 38aaeb29e3STom Joseph * Initialize the IPMI socket object with the socket descriptor 39aaeb29e3STom Joseph * 407a0142c5SVernon Mauery * @param [in] pointer to a boost::asio udp socket object 41aaeb29e3STom Joseph * 42aaeb29e3STom Joseph * @return None 43aaeb29e3STom Joseph */ Channel(std::shared_ptr<boost::asio::ip::udp::socket> socket)447a0142c5SVernon Mauery explicit Channel(std::shared_ptr<boost::asio::ip::udp::socket> socket) : 457a0142c5SVernon Mauery socket(socket) 46bc8958feSGeorge Liu {} 479979e997SRajashekar Gade Reddy /** 489979e997SRajashekar Gade Reddy * @brief Check if ip address is ipv4 mapped ipv6 499979e997SRajashekar Gade Reddy * 509979e997SRajashekar Gade Reddy * @param v6Addr : in6_addr obj 519979e997SRajashekar Gade Reddy * 529979e997SRajashekar Gade Reddy * @return true if ipv4 mapped ipv6 else return false 539979e997SRajashekar Gade Reddy */ isIpv4InIpv6(const struct in6_addr & v6Addr) const549979e997SRajashekar Gade Reddy bool isIpv4InIpv6(const struct in6_addr& v6Addr) const 559979e997SRajashekar Gade Reddy { 569979e997SRajashekar Gade Reddy constexpr uint8_t prefix[v4v6Index] = {0, 0, 0, 0, 0, 0, 579979e997SRajashekar Gade Reddy 0, 0, 0, 0, 0xff, 0xff}; 589979e997SRajashekar Gade Reddy return 0 == std::memcmp(&v6Addr.s6_addr[0], &prefix[0], sizeof(prefix)); 599979e997SRajashekar Gade Reddy } 60aaeb29e3STom Joseph /** 61aaeb29e3STom Joseph * @brief Fetch the IP address of the remote peer 62aaeb29e3STom Joseph * 639979e997SRajashekar Gade Reddy * @param remoteIpv4Addr : ipv4 address is assigned to it. 649979e997SRajashekar Gade Reddy * 65aaeb29e3STom Joseph * Returns the IP address of the remote peer which is connected to this 66aaeb29e3STom Joseph * socket 67aaeb29e3STom Joseph * 68aaeb29e3STom Joseph * @return IP address of the remote peer 69aaeb29e3STom Joseph */ getRemoteAddress(uint32_t & remoteIpv4Addr) const709979e997SRajashekar Gade Reddy std::string getRemoteAddress(uint32_t& remoteIpv4Addr) const 717a0142c5SVernon Mauery { 72d92bc324SVernon Mauery const char* retval = nullptr; 73d92bc324SVernon Mauery if (sockAddrSize == sizeof(sockaddr_in)) 74d92bc324SVernon Mauery { 75d92bc324SVernon Mauery char ipv4addr[INET_ADDRSTRLEN]; 769979e997SRajashekar Gade Reddy const sockaddr_in* sa = 779979e997SRajashekar Gade Reddy reinterpret_cast<const sockaddr_in*>(&remoteSockAddr); 789979e997SRajashekar Gade Reddy remoteIpv4Addr = sa->sin_addr.s_addr; 79*8425624aSPatrick Williams retval = 80*8425624aSPatrick Williams inet_ntop(AF_INET, &(sa->sin_addr), ipv4addr, sizeof(ipv4addr)); 81d92bc324SVernon Mauery } 82d92bc324SVernon Mauery else if (sockAddrSize == sizeof(sockaddr_in6)) 83d92bc324SVernon Mauery { 84d92bc324SVernon Mauery char ipv6addr[INET6_ADDRSTRLEN]; 859979e997SRajashekar Gade Reddy const sockaddr_in6* sa = 869979e997SRajashekar Gade Reddy reinterpret_cast<const sockaddr_in6*>(&remoteSockAddr); 879979e997SRajashekar Gade Reddy 889979e997SRajashekar Gade Reddy if (isIpv4InIpv6(sa->sin6_addr)) 899979e997SRajashekar Gade Reddy { 909979e997SRajashekar Gade Reddy std::copy_n(&sa->sin6_addr.s6_addr[v4v6Index], 919979e997SRajashekar Gade Reddy sizeof(remoteIpv4Addr), 929979e997SRajashekar Gade Reddy reinterpret_cast<uint8_t*>(&remoteIpv4Addr)); 93d92bc324SVernon Mauery } 949979e997SRajashekar Gade Reddy retval = inet_ntop(AF_INET6, &(sa->sin6_addr), ipv6addr, 959979e997SRajashekar Gade Reddy sizeof(ipv6addr)); 969979e997SRajashekar Gade Reddy } 979979e997SRajashekar Gade Reddy 98d92bc324SVernon Mauery if (retval) 99d92bc324SVernon Mauery { 100d92bc324SVernon Mauery return retval; 101d92bc324SVernon Mauery } 1027b7f25f7SGeorge Liu lg2::error("Error in inet_ntop: {ERROR}", "ERROR", strerror(errno)); 103d92bc324SVernon Mauery return std::string(); 1047a0142c5SVernon Mauery } 105aaeb29e3STom Joseph 106aaeb29e3STom Joseph /** 107aaeb29e3STom Joseph * @brief Fetch the port number of the remote peer 108aaeb29e3STom Joseph * 109aaeb29e3STom Joseph * Returns the port number of the remote peer 110aaeb29e3STom Joseph * 111aaeb29e3STom Joseph * @return Port number 112aaeb29e3STom Joseph * 113aaeb29e3STom Joseph */ getPort() const114d92bc324SVernon Mauery uint16_t getPort() const 115aaeb29e3STom Joseph { 116d92bc324SVernon Mauery if (sockAddrSize == sizeof(sockaddr_in)) 117d92bc324SVernon Mauery { 118d92bc324SVernon Mauery return ntohs(reinterpret_cast<const sockaddr_in*>(&remoteSockAddr) 119d92bc324SVernon Mauery ->sin_port); 120d92bc324SVernon Mauery } 121d92bc324SVernon Mauery if (sockAddrSize == sizeof(sockaddr_in6)) 122d92bc324SVernon Mauery { 123d92bc324SVernon Mauery return ntohs(reinterpret_cast<const sockaddr_in6*>(&remoteSockAddr) 124d92bc324SVernon Mauery ->sin6_port); 125d92bc324SVernon Mauery } 126d92bc324SVernon Mauery return 0; 127aaeb29e3STom Joseph } 128aaeb29e3STom Joseph 129aaeb29e3STom Joseph /** 130aaeb29e3STom Joseph * @brief Read the incoming packet 131aaeb29e3STom Joseph * 132aaeb29e3STom Joseph * Reads the data available on the socket 133aaeb29e3STom Joseph * 134aaeb29e3STom Joseph * @return A tuple with return code and vector with the buffer 135aaeb29e3STom Joseph * In case of success, the vector is populated with the data 136aaeb29e3STom Joseph * available on the socket and return code is 0. 137aaeb29e3STom Joseph * In case of error, the return code is < 0 and vector is set 138aaeb29e3STom Joseph * to size 0. 139aaeb29e3STom Joseph */ read()1407a0142c5SVernon Mauery std::tuple<int, std::vector<uint8_t>> read() 1417a0142c5SVernon Mauery { 142d92bc324SVernon Mauery // cannot use the standard asio reading mechanism because it does not 143d92bc324SVernon Mauery // provide a mechanism to reach down into the depths and use a msghdr 1447a0142c5SVernon Mauery std::vector<uint8_t> packet(socket->available()); 145d92bc324SVernon Mauery iovec iov = {packet.data(), packet.size()}; 146d92bc324SVernon Mauery char msgCtrl[1024]; 147d92bc324SVernon Mauery msghdr msg = {&remoteSockAddr, sizeof(remoteSockAddr), &iov, 1, 148d92bc324SVernon Mauery msgCtrl, sizeof(msgCtrl), 0}; 149d92bc324SVernon Mauery 150d92bc324SVernon Mauery ssize_t bytesReceived = recvmsg(socket->native_handle(), &msg, 0); 151d92bc324SVernon Mauery // Read of the packet failed 152d92bc324SVernon Mauery if (bytesReceived < 0) 1537a0142c5SVernon Mauery { 154d92bc324SVernon Mauery // something bad happened; bail 1557b7f25f7SGeorge Liu lg2::error("Error in recvmsg: {ERROR}", "ERROR", 1567b7f25f7SGeorge Liu strerror(-bytesReceived)); 157d92bc324SVernon Mauery return std::make_tuple(-errno, std::vector<uint8_t>()); 1587a0142c5SVernon Mauery } 159d92bc324SVernon Mauery // save the size of either ipv4 or i4v6 sockaddr 160d92bc324SVernon Mauery sockAddrSize = msg.msg_namelen; 161d92bc324SVernon Mauery 162d92bc324SVernon Mauery // extract the destination address from the message 163d92bc324SVernon Mauery cmsghdr* cmsg; 164d92bc324SVernon Mauery for (cmsg = CMSG_FIRSTHDR(&msg); cmsg != 0; 165d92bc324SVernon Mauery cmsg = CMSG_NXTHDR(&msg, cmsg)) 1667a0142c5SVernon Mauery { 167d92bc324SVernon Mauery if (cmsg->cmsg_level == IPPROTO_IP && cmsg->cmsg_type == IP_PKTINFO) 168d92bc324SVernon Mauery { 169d92bc324SVernon Mauery // save local address from the pktinfo4 170d92bc324SVernon Mauery pktinfo4 = *reinterpret_cast<in_pktinfo*>(CMSG_DATA(cmsg)); 171d92bc324SVernon Mauery } 172d92bc324SVernon Mauery if (cmsg->cmsg_level == IPPROTO_IPV6 && 173d92bc324SVernon Mauery cmsg->cmsg_type == IPV6_PKTINFO) 174d92bc324SVernon Mauery { 175d92bc324SVernon Mauery // save local address from the pktinfo6 176d92bc324SVernon Mauery pktinfo6 = *reinterpret_cast<in6_pktinfo*>(CMSG_DATA(cmsg)); 177d92bc324SVernon Mauery } 1787a0142c5SVernon Mauery } 1797a0142c5SVernon Mauery return std::make_tuple(0, packet); 1807a0142c5SVernon Mauery } 181aaeb29e3STom Joseph 182aaeb29e3STom Joseph /** 183aaeb29e3STom Joseph * @brief Write the outgoing packet 184aaeb29e3STom Joseph * 185aaeb29e3STom Joseph * Writes the data in the vector to the socket 186aaeb29e3STom Joseph * 187aaeb29e3STom Joseph * @param [in] inBuffer 188aaeb29e3STom Joseph * The vector would be the buffer of data to write to the socket. 189aaeb29e3STom Joseph * 190d92bc324SVernon Mauery * @return In case of success the return code is the number of bytes 191d92bc324SVernon Mauery * written and return code is < 0 in case of failure. 192aaeb29e3STom Joseph */ write(const std::vector<uint8_t> & inBuffer)1937a0142c5SVernon Mauery int write(const std::vector<uint8_t>& inBuffer) 1947a0142c5SVernon Mauery { 195d92bc324SVernon Mauery // in order to make sure packets go back out from the same 196d92bc324SVernon Mauery // IP address they came in on, sendmsg must be used instead 197d92bc324SVernon Mauery // of the boost::asio::ip::send or sendto 198d92bc324SVernon Mauery iovec iov = {const_cast<uint8_t*>(inBuffer.data()), inBuffer.size()}; 199d92bc324SVernon Mauery char msgCtrl[1024]; 200d92bc324SVernon Mauery msghdr msg = {&remoteSockAddr, sockAddrSize, &iov, 1, 201d92bc324SVernon Mauery msgCtrl, sizeof(msgCtrl), 0}; 202d92bc324SVernon Mauery int cmsg_space = 0; 203d92bc324SVernon Mauery cmsghdr* cmsg = CMSG_FIRSTHDR(&msg); 204d92bc324SVernon Mauery if (pktinfo6) 2057a0142c5SVernon Mauery { 206d92bc324SVernon Mauery cmsg->cmsg_level = IPPROTO_IPV6; 207d92bc324SVernon Mauery cmsg->cmsg_type = IPV6_PKTINFO; 208d92bc324SVernon Mauery cmsg->cmsg_len = CMSG_LEN(sizeof(in6_pktinfo)); 209d92bc324SVernon Mauery *reinterpret_cast<in6_pktinfo*>(CMSG_DATA(cmsg)) = *pktinfo6; 210d92bc324SVernon Mauery cmsg_space += CMSG_SPACE(sizeof(in6_pktinfo)); 2117a0142c5SVernon Mauery } 212d92bc324SVernon Mauery else if (pktinfo4) 2137a0142c5SVernon Mauery { 214d92bc324SVernon Mauery cmsg->cmsg_level = IPPROTO_IP; 215d92bc324SVernon Mauery cmsg->cmsg_type = IP_PKTINFO; 216d92bc324SVernon Mauery cmsg->cmsg_len = CMSG_LEN(sizeof(in_pktinfo)); 217d92bc324SVernon Mauery *reinterpret_cast<in_pktinfo*>(CMSG_DATA(cmsg)) = *pktinfo4; 218d92bc324SVernon Mauery cmsg_space += CMSG_SPACE(sizeof(in_pktinfo)); 2197a0142c5SVernon Mauery } 220d92bc324SVernon Mauery msg.msg_controllen = cmsg_space; 221d92bc324SVernon Mauery int ret = sendmsg(socket->native_handle(), &msg, 0); 222d92bc324SVernon Mauery if (ret < 0) 223d92bc324SVernon Mauery { 2247b7f25f7SGeorge Liu lg2::error("Error in sendmsg: {ERROR}", "ERROR", strerror(-ret)); 225d92bc324SVernon Mauery } 226d92bc324SVernon Mauery return ret; 2277a0142c5SVernon Mauery } 228aaeb29e3STom Joseph 229aaeb29e3STom Joseph /** 230aaeb29e3STom Joseph * @brief Returns file descriptor for the socket 231aaeb29e3STom Joseph */ getHandle(void) const232aaeb29e3STom Joseph auto getHandle(void) const 233aaeb29e3STom Joseph { 2347a0142c5SVernon Mauery return socket->native_handle(); 235aaeb29e3STom Joseph } 236aaeb29e3STom Joseph 237aaeb29e3STom Joseph private: 2387a0142c5SVernon Mauery std::shared_ptr<boost::asio::ip::udp::socket> socket; 239d92bc324SVernon Mauery sockaddr_storage remoteSockAddr; 240d92bc324SVernon Mauery socklen_t sockAddrSize; 241d92bc324SVernon Mauery std::optional<in_pktinfo> pktinfo4; 242d92bc324SVernon Mauery std::optional<in6_pktinfo> pktinfo6; 243aaeb29e3STom Joseph }; 244aaeb29e3STom Joseph 245aaeb29e3STom Joseph } // namespace udpsocket 246