// Copyright (c) 2019 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef QUICHE_QUIC_QBONE_PLATFORM_RTNETLINK_MESSAGE_H_
#define QUICHE_QUIC_QBONE_PLATFORM_RTNETLINK_MESSAGE_H_

#include <linux/netlink.h>
#include <linux/rtnetlink.h>
#include <stdint.h>
#include <sys/socket.h>
#include <sys/uio.h>

#include <memory>
#include <vector>

#include "quiche/quic/platform/api/quic_logging.h"

namespace quic {

// This base class is used to construct an array struct iovec that represents a
// rtnetlink message as defined in man 7 rtnet. Padding for message header
// alignment to conform NLMSG_* and RTA_* macros is added at the end of each
// iovec::iov_base.
class RtnetlinkMessage {
 public:
  virtual ~RtnetlinkMessage();

  enum class Operation {
    NEW,
    DEL,
    GET,
  };

  // Appends a struct rtattr to the message. nlmsg_len and rta_len is handled
  // properly.
  // Override this to perform check on type.
  virtual void AppendAttribute(uint16_t type, const void* data,
                               uint16_t data_length);

  // Builds the array of iovec that can be fed into sendmsg directly.
  std::unique_ptr<struct iovec[]> BuildIoVec() const;

  // The size of the array of iovec if BuildIovec is called.
  size_t IoVecSize() const;

 protected:
  // Subclass should add their own message header immediately after the
  // nlmsghdr. Make this private to force the creation of such header.
  RtnetlinkMessage(uint16_t type, uint16_t flags, uint32_t seq, uint32_t pid,
                   const void* payload_header, size_t payload_header_length);

  // Adjusts nlmsg_len in the header assuming additional_data_length is appended
  // at the end.
  void AdjustMessageLength(size_t additional_data_length);

 private:
  // Convenient function for accessing the nlmsghdr.
  struct nlmsghdr* MessageHeader();

  std::vector<struct iovec> message_;
};

// Message for manipulating link level configuration as defined in man 7
// rtnetlink. RTM_NEWLINK, RTM_DELLINK and RTM_GETLINK are supported.
class LinkMessage : public RtnetlinkMessage {
 public:
  static LinkMessage New(RtnetlinkMessage::Operation request_operation,
                         uint16_t flags, uint32_t seq, uint32_t pid,
                         const struct ifinfomsg* interface_info_header);

 private:
  using RtnetlinkMessage::RtnetlinkMessage;
};

// Message for manipulating address level configuration as defined in man 7
// rtnetlink. RTM_NEWADDR, RTM_NEWADDR and RTM_GETADDR are supported.
class AddressMessage : public RtnetlinkMessage {
 public:
  static AddressMessage New(RtnetlinkMessage::Operation request_operation,
                            uint16_t flags, uint32_t seq, uint32_t pid,
                            const struct ifaddrmsg* interface_address_header);

 private:
  using RtnetlinkMessage::RtnetlinkMessage;
};

// Message for manipulating routing table as defined in man 7 rtnetlink.
// RTM_NEWROUTE, RTM_DELROUTE and RTM_GETROUTE are supported.
class RouteMessage : public RtnetlinkMessage {
 public:
  static RouteMessage New(RtnetlinkMessage::Operation request_operation,
                          uint16_t flags, uint32_t seq, uint32_t pid,
                          const struct rtmsg* route_message_header);

 private:
  using RtnetlinkMessage::RtnetlinkMessage;
};

class RuleMessage : public RtnetlinkMessage {
 public:
  static RuleMessage New(RtnetlinkMessage::Operation request_operation,
                         uint16_t flags, uint32_t seq, uint32_t pid,
                         const struct rtmsg* rule_message_header);

 private:
  using RtnetlinkMessage::RtnetlinkMessage;
};

}  // namespace quic

#endif  // QUICHE_QUIC_QBONE_PLATFORM_RTNETLINK_MESSAGE_H_
