// Copyright 2019 The gVisor Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

// There are a number of structs and values that we can't #include because of a
// difference between C and C++ (C++ won't let you implicitly cast from void* to
// struct something*). We re-define them here.

#ifndef GVISOR_TEST_SYSCALLS_IPTABLES_TYPES_H_
#define GVISOR_TEST_SYSCALLS_IPTABLES_TYPES_H_

// Netfilter headers require some headers to precede them.
// clang-format off
#include <netinet/in.h>
#include <stddef.h>
// clang-format on

#include <linux/netfilter/x_tables.h>
#include <linux/netfilter_ipv4.h>
#include <linux/netfilter_ipv6.h>
#include <net/if.h>
#include <netinet/ip.h>
#include <stdint.h>

//
// IPv4 ABI.
//

#define ipt_standard_target xt_standard_target
#define ipt_entry_target xt_entry_target
#define ipt_error_target xt_error_target

enum SockOpts {
  // For setsockopt.
  IPT_BASE_CTL = 64,
  IPT_SO_SET_REPLACE = IPT_BASE_CTL,
  IPT_SO_SET_ADD_COUNTERS = IPT_BASE_CTL + 1,
  IPT_SO_SET_MAX = IPT_SO_SET_ADD_COUNTERS,

  // For getsockopt.
  IPT_SO_GET_INFO = IPT_BASE_CTL,
  IPT_SO_GET_ENTRIES = IPT_BASE_CTL + 1,
  IPT_SO_GET_REVISION_MATCH = IPT_BASE_CTL + 2,
  IPT_SO_GET_REVISION_TARGET = IPT_BASE_CTL + 3,
  IPT_SO_GET_MAX = IPT_SO_GET_REVISION_TARGET
};

// ipt_ip specifies basic matching criteria that can be applied by examining
// only the IP header of a packet.
struct ipt_ip {
  // Source IP address.
  struct in_addr src;

  // Destination IP address.
  struct in_addr dst;

  // Source IP address mask.
  struct in_addr smsk;

  // Destination IP address mask.
  struct in_addr dmsk;

  // Input interface.
  char iniface[IFNAMSIZ];

  // Output interface.
  char outiface[IFNAMSIZ];

  // Input interface mask.
  unsigned char iniface_mask[IFNAMSIZ];

  // Output interface mask.
  unsigned char outiface_mask[IFNAMSIZ];

  // Transport protocol.
  uint16_t proto;

  // Flags.
  uint8_t flags;

  // Inverse flags.
  uint8_t invflags;
};

// ipt_entry is an iptables rule. It contains information about what packets the
// rule matches and what action (target) to perform for matching packets.
struct ipt_entry {
  // Basic matching information used to match a packet's IP header.
  struct ipt_ip ip;

  // A caching field that isn't used by userspace.
  unsigned int nfcache;

  // The number of bytes between the start of this ipt_entry struct and the
  // rule's target.
  uint16_t target_offset;

  // The total size of this rule, from the beginning of the entry to the end of
  // the target.
  uint16_t next_offset;

  // A return pointer not used by userspace.
  unsigned int comefrom;

  // Counters for packets and bytes, which we don't yet implement.
  struct xt_counters counters;

  // The data for all this rules matches followed by the target. This runs
  // beyond the value of sizeof(struct ipt_entry).
  unsigned char elems[0];
};

// Passed to getsockopt(IPT_SO_GET_INFO).
struct ipt_getinfo {
  // The name of the table. The user only fills this in, the rest is filled in
  // when returning from getsockopt. Currently "nat" and "mangle" are supported.
  char name[XT_TABLE_MAXNAMELEN];

  // A bitmap of which hooks apply to the table. For example, a table with hooks
  // PREROUTING and FORWARD has the value
  // (1 << NF_IP_PRE_REOUTING) | (1 << NF_IP_FORWARD).
  unsigned int valid_hooks;

  // The offset into the entry table for each valid hook. The entry table is
  // returned by getsockopt(IPT_SO_GET_ENTRIES).
  unsigned int hook_entry[NF_IP_NUMHOOKS];

  // For each valid hook, the underflow is the offset into the entry table to
  // jump to in case traversing the table yields no verdict (although I have no
  // clue how that could happen - builtin chains always end with a policy, and
  // user-defined chains always end with a RETURN.
  //
  // The entry referred to must be an "unconditional" entry, meaning it has no
  // matches, specifies no IP criteria, and either DROPs or ACCEPTs packets.  It
  // basically has to be capable of making a definitive decision no matter what
  // it's passed.
  unsigned int underflow[NF_IP_NUMHOOKS];

  // The number of entries in the entry table returned by
  // getsockopt(IPT_SO_GET_ENTRIES).
  unsigned int num_entries;

  // The size of the entry table returned by getsockopt(IPT_SO_GET_ENTRIES).
  unsigned int size;
};

// Passed to getsockopt(IPT_SO_GET_ENTRIES).
struct ipt_get_entries {
  // The name of the table. The user fills this in. Currently "nat" and "mangle"
  // are supported.
  char name[XT_TABLE_MAXNAMELEN];

  // The size of the entry table in bytes. The user fills this in with the value
  // from struct ipt_getinfo.size.
  unsigned int size;

  // The entries for the given table. This will run past the size defined by
  // sizeof(struct ipt_get_entries).
  struct ipt_entry entrytable[0];
};

// Passed to setsockopt(SO_SET_REPLACE).
struct ipt_replace {
  // The name of the table.
  char name[XT_TABLE_MAXNAMELEN];

  // The same as struct ipt_getinfo.valid_hooks. Users don't change this.
  unsigned int valid_hooks;

  // The same as struct ipt_getinfo.num_entries.
  unsigned int num_entries;

  // The same as struct ipt_getinfo.size.
  unsigned int size;

  // The same as struct ipt_getinfo.hook_entry.
  unsigned int hook_entry[NF_IP_NUMHOOKS];

  // The same as struct ipt_getinfo.underflow.
  unsigned int underflow[NF_IP_NUMHOOKS];

  // The number of counters, which should equal the number of entries.
  unsigned int num_counters;

  // The unchanged values from each ipt_entry's counters.
  struct xt_counters* counters;

  // The entries to write to the table. This will run past the size defined by
  // sizeof(srtuct ipt_replace);
  struct ipt_entry entries[0];
};

//
// IPv6 ABI.
//

enum SockOpts6 {
  // For setsockopt.
  IP6T_BASE_CTL = 64,
  IP6T_SO_SET_REPLACE = IP6T_BASE_CTL,
  IP6T_SO_SET_ADD_COUNTERS = IP6T_BASE_CTL + 1,
  IP6T_SO_SET_MAX = IP6T_SO_SET_ADD_COUNTERS,

  // For getsockopt.
  IP6T_SO_GET_INFO = IP6T_BASE_CTL,
  IP6T_SO_GET_ENTRIES = IP6T_BASE_CTL + 1,
  IP6T_SO_GET_REVISION_MATCH = IP6T_BASE_CTL + 4,
  IP6T_SO_GET_REVISION_TARGET = IP6T_BASE_CTL + 5,
  IP6T_SO_GET_MAX = IP6T_SO_GET_REVISION_TARGET
};

// ip6t_ip6 specifies basic matching criteria that can be applied by examining
// only the IP header of a packet.
// Must be POD so that it can be zero-initialized using memset() because
// initializing by "{}" leaves it partially uninitialized for paddings and
// nested unions.
struct ip6t_ip6 {
  // Source IP address.
  struct in6_addr src;

  // Destination IP address.
  struct in6_addr dst;

  // Source IP address mask.
  struct in6_addr smsk;

  // Destination IP address mask.
  struct in6_addr dmsk;

  // Input interface.
  char iniface[IFNAMSIZ];

  // Output interface.
  char outiface[IFNAMSIZ];

  // Input interface mask.
  unsigned char iniface_mask[IFNAMSIZ];

  // Output interface mask.
  unsigned char outiface_mask[IFNAMSIZ];

  // Transport protocol.
  uint16_t proto;

  // TOS.
  uint8_t tos;

  // Flags.
  uint8_t flags;

  // Inverse flags.
  uint8_t invflags;
};

// ip6t_entry is an ip6tables rule.
struct ip6t_entry {
  // Basic matching information used to match a packet's IP header.
  struct ip6t_ip6 ipv6;

  // A caching field that isn't used by userspace.
  unsigned int nfcache;

  // The number of bytes between the start of this entry and the rule's target.
  uint16_t target_offset;

  // The total size of this rule, from the beginning of the entry to the end of
  // the target.
  uint16_t next_offset;

  // A return pointer not used by userspace.
  unsigned int comefrom;

  // Counters for packets and bytes, which we don't yet implement.
  struct xt_counters counters;

  // The data for all this rules matches followed by the target. This runs
  // beyond the value of sizeof(struct ip6t_entry).
  unsigned char elems[0];
};

// Passed to getsockopt(IP6T_SO_GET_ENTRIES).
struct ip6t_get_entries {
  // The name of the table.
  char name[XT_TABLE_MAXNAMELEN];

  // The size of the entry table in bytes. The user fills this in with the value
  // from struct ipt_getinfo.size.
  unsigned int size;

  // The entries for the given table. This will run past the size defined by
  // sizeof(struct ip6t_get_entries).
  struct ip6t_entry entrytable[0];
};

// Passed to setsockopt(IP6T_SO_SET_REPLACE).
struct ip6t_replace {
  char name[XT_TABLE_MAXNAMELEN];
  unsigned int valid_hooks;
  unsigned int num_entries;
  unsigned int size;
  unsigned int hook_entry[NF_IP_NUMHOOKS];
  unsigned int underflow[NF_IP_NUMHOOKS];
  unsigned int num_counters;
  struct xt_counters* counters;
  struct ip6t_entry entries[0];
};

#endif  // GVISOR_TEST_SYSCALLS_IPTABLES_TYPES_H_
