11. 查询 libiptc 的函数

本节介绍哪些函数允许您查询 libiptc。我们将使用 libiptc 的头文件,文件usr/local/include/libiptc/libiptc.h,其中包含每个函数的原型,作为我们解释的参考。

我还包含了从 Linux netfilter Hacking HOWTO 中获取的简要描述(如果可用),在每个函数说明中。

11.1. iptc_init

名称: iptc_init

用法: 获取规则的快照。

原型: iptc_handle_t iptc_init(const char *tablename)

描述: 在调用任何其他函数之前,必须先调用此函数作为初始化器。

参数: tablename 是我们需要查询和/或修改的表的名称;这可以是 filtermanglenat 等。

返回值: 指向 iptc_handle_t 类型结构的指针,该指针必须用作我们从 libiptc 调用的其余函数的主要参数。iptc_init 返回指向结构的指针,如果失败则返回 NULL。如果发生这种情况,您可以调用 iptc_strerror 来获取有关错误的信息。请参见下文。

请查看文件中的这段代码iptables-save.c了解如何调用此函数

  h = iptc_init(tablename);
  if (!h)
    exit_error(OTHER_PROBLEM, "Can't initialize: %s\n",iptc_strerror(errno));

11.2. iptc_strerror

名称: iptc_strerror

用法: 将错误编号转换为更易于人阅读的形式。

原型: const char *iptc_strerror(int err)

描述: 此函数返回 iptc 库中失败代码的更具意义的解释。如果函数失败,它将始终设置 errno。可以将此值传递给 iptc_strerror() 以产生错误消息。

参数: err 是一个指示错误编号的整数。

返回值: 包含错误描述的字符指针。

11.3. iptc_first_chain

名称: iptc_first_chain

用法: 遍历链的迭代器函数。

原型: const char *iptc_first_chain(iptc_handle_t *handle)

描述: 此函数返回表中的第一个链名称。

参数: 指向 iptc_handle_t 类型结构的指针,该指针是通过先前调用 iptc_init 获得的。

返回值: 指向链名称的字符指针。

11.4. iptc_next_chain

名称: iptc_next_chain

用法: 遍历链的迭代器函数。

原型: const char *iptc_next_chain(iptc_handle_t *handle)

描述: 此函数返回表中的下一个链名称;NULL 表示没有更多链。

参数: 指向 iptc_handle_t 类型结构的指针,该指针是通过先前调用 iptc_init 获得的。

返回值: 指向链名称的字符指针。

前两个函数允许我们遍历表的链,获取每个链的名称;iptc_first_chain 返回表的第一个链的名称;iptc_next_chain 返回下一个链的名称,当函数到达末尾时返回 NULL。

我们可以创建程序 #1 来练习我们对前面四个函数的理解

/*
 * How to use libiptc- program #1
 * /usr/local/src/p1.c
 */

#include <getopt.h>
#include <sys/errno.h>
#include <stdio.h>
#include <fcntl.h>
#include <stdlib.h>
#include <string.h>
#include <dlfcn.h>
#include <time.h>
#include "libiptc/libiptc.h"
#include "iptables.h"

int main(void)
{
  iptc_handle_t h;
  const char *chain = NULL;
  const char *tablename = "filter";

  program_name = "p1";
  program_version = NETFILTER_VERSION;

  h = iptc_init(tablename);
  if ( !h )   {
     printf("Error initializing: %s\n", iptc_strerror(errno));
    exit(errno);
  }

  for (chain = iptc_first_chain(&h); chain; chain = iptc_next_chain(&h))  {
    printf("%s\n", chain);
  }

  exit(0);

} /* main */

编写此程序并将其另存为p1.c/usr/local/src.

现在编写这个 "bash" 脚本以简化编译过程

#!/bin/bash

gcc -Wall -Wunused -DNETFILTER_VERSION=\"1.2.6\" -rdynamic -o $1 $1.c \
/usr/local/lib/iptables.o /usr/local/lib/libiptc.a -ldl

将其另存为ipt-cc并且不要忘记 chmod 0700 ipt-cc

现在编译您的 p1 程序

bash# ./ipt-cc p1

并运行它

bash# ./p1

您将得到

INPUT
FORWARD
OUTPUT

这些是三个内置的 iptables 链。

现在使用 iptables 创建一些新链并再次运行您的程序

bash# iptables -N chain_1
bash# iptables -N chain_2
bash# ./p1

您将得到

INPUT
FORWARD
OUTPUT
chain_1
chain_2

尝试生成一个错误,将 tablename 初始化为 myfilter 而不是 filter。当您再次编译并执行程序时,您将得到

Error initializing: Table does not exist (do you need to insmod?)

iptables 通知您 myfilter 不作为表存在。

11.5. iptc_is_chain

名称: iptc_is_chain

用法: 检查链是否存在。

原型: int iptc_is_chain(const char *chain, const iptc_handle_t handle)

描述: 此函数检查参数 chain 中描述的链是否在表中存在。

参数: chain 是一个字符指针,包含我们要检查的链的名称。handle 是指向 iptc_handle_t 类型结构的指针,该指针是通过先前调用 iptc_init 获得的。

返回值: 整数值 1(真),如果链存在;整数值 0(假),如果链不存在。

11.6. iptc_builtin

名称: iptc_builtin

用法: 这是一个内置链吗?

原型: int iptc_builtin(const char *chain, const iptc_handle_t handle)

描述: 此函数用于检查给定的链名称是否为内置链。

参数: chain 是一个字符指针,包含我们要检查的链的名称。handle 是指向 iptc_handle_t 类型结构的指针,该指针是通过先前调用 iptc_init 获得的。

返回值: 如果给定的链名称是内置链的名称,则返回整数值 1(真);如果不是,则返回整数值 0(假)。

11.7. iptc_first_rule

名称: iptc_first_rule

用法: 获取给定链中的第一个规则。

原型: const struct ipt_entry *iptc_first_rule(const char *chain, iptc_handle_t *handle)

描述: 此函数返回指向给定链名称中第一个规则的指针;对于空链,返回 NULL。

参数: chain 是一个字符指针,包含我们要获取规则的链的名称。handle 是指向 iptc_handle_t 类型结构的指针,该指针是通过先前调用 iptc_init 获得的。

返回值: 返回指向 ipt_entry 结构的指针,该结构包含有关链的第一个规则的信息。有关此结构的说明,请参见下文。

11.8. iptc_next_rule

名称: iptc_next_rule

用法: 获取给定链中的下一个规则。

原型: const struct ipt_entry *iptc_next_rule(const struct ipt_entry *prev, iptc_handle_t *handle)

描述: 此函数返回指向给定链名称中下一个规则的指针;NULL 表示链的末尾。

参数: prev 是指向 ipt_entry 类型结构的指针,必须首先通过先前调用函数 iptc_first_rule 获得。为了获得第二个和后续规则,您必须传递一个指向包含有关链中上一个规则信息的结构的指针。handle 是指向 iptc_handle_t 类型结构的指针,该指针是通过先前调用 iptc_init 获得的。

返回值: 返回指向 ipt_entry 结构的指针,该结构包含有关链的下一个规则的信息。有关此结构的说明,请参见下文。

11.9. iptc_get_target

名称: iptc_get_target

用法: 获取指向此条目目标名称的指针。

原型: const char *iptc_get_target(const struct ipt_entry *e, iptc_handle_t *handle)

描述: 此函数获取给定规则的目标。如果是扩展目标,则返回该目标的名称。如果是跳转到另一个链,则返回该链的名称。如果是判决(例如 DROP),则返回该名称。如果它没有目标(会计风格的规则),则返回空字符串。请注意,应使用此函数代替直接使用 ipt_entry 结构的 verdict 字段的值,因为它提供了对标准判决的上述进一步解释。

参数: e 是指向 ipt_entry 类型结构的指针,必须首先通过先前调用函数 iptc_first_rule 或函数 iptc_next_rule 获得。handle 是指向 iptc_handle_t 类型结构的指针,该指针是通过先前调用 iptc_init 获得的。

返回值: 返回指向目标名称的字符指针。有关更多信息,请参见上面的描述

现在是时候解释 ipt_entry 结构了;以下代码片段取自 iptables 包的源代码

/* Internet address. */
struct in_addr {
  __u32  s_addr;
};

/* Yes, Virginia, you have to zero the padding. */
struct ipt_ip {
  /* Source and destination IP addr */
  struct in_addr src, dst;
  /* Mask for src and dest IP addr */
  struct in_addr smsk, dmsk;
  char iniface[IFNAMSIZ], outiface[IFNAMSIZ];
  unsigned char iniface_mask[IFNAMSIZ], outiface_mask[IFNAMSIZ];

  /* Protocol, 0 = ANY */
  u_int16_t proto;

  /* Flags word */
  u_int8_t flags;
  /* Inverse flags */
  u_int8_t invflags;
};

struct ipt_counters
{
  u_int64_t pcnt, bcnt;      /* Packet and byte counters */
};

/* This structure defines each of the firewall rules.  Consists of 3
   parts which are 1) general IP header stuff 2) match specific
   stuff 3) the target to perform if the rule matches */
struct ipt_entry
{
  struct ipt_ip ip;

  /* Mark with fields that we care about. */
  unsigned int nfcache;

  /* Size of ipt_entry + matches */
  u_int16_t target_offset;
  /* Size of ipt_entry + matches + target */
  u_int16_t next_offset;

  /* Back pointer */
  unsigned int comefrom;

  /* Packet and byte counters. */
  struct ipt_counters counters;

  /* The matches (if any), then the target. */
  unsigned char elems[0];
};

ipt_entry 结构包含

使用所有这些信息的简单方法是从以下位置借用一些函数iptables-save.cPaul Russell 和 Harald Welte。

这是另一个示例程序 程序 #2,在 Russell-Welte 的大力帮助下编写

/* 
 * How to use libiptc- program #2
 * /usr/local/src/p1.c
 */

#include <getopt.h>
#include <sys/errno.h>
#include <stdio.h>
#include <fcntl.h>
#include <stdlib.h>
#include <string.h>
#include <dlfcn.h>
#include <time.h>
#include "libiptc/libiptc.h"
#include "iptables.h"

/* Here begins some of the code taken from iptables-save.c **************** */
#define IP_PARTS_NATIVE(n)      \
(unsigned int)((n)>>24)&0xFF,   \
(unsigned int)((n)>>16)&0xFF,   \
(unsigned int)((n)>>8)&0xFF,    \
(unsigned int)((n)&0xFF)

#define IP_PARTS(n) IP_PARTS_NATIVE(ntohl(n))

/* This assumes that mask is contiguous, and byte-bounded. */
static void
print_iface(char letter, const char *iface, const unsigned char *mask,
      int invert)
{
  unsigned int i;

  if (mask[0] == 0)
    return;

  printf("-%c %s", letter, invert ? "! " : "");

  for (i = 0; i < IFNAMSIZ; i++) {
    if (mask[i] != 0) {
      if (iface[i] != '\0')
        printf("%c", iface[i]);
    } else {
      /* we can access iface[i-1] here, because 
       * a few lines above we make sure that mask[0] != 0 */
      if (iface[i-1] != '\0')
        printf("+");
      break;
    }
  }

  printf(" ");
}

/* These are hardcoded backups in iptables.c, so they are safe */
struct pprot {
  char *name;
  u_int8_t num;
};

/* FIXME: why don't we use /etc/protocols ? */
static const struct pprot chain_protos[] = {
  { "tcp", IPPROTO_TCP },
  { "udp", IPPROTO_UDP },
  { "icmp", IPPROTO_ICMP },
  { "esp", IPPROTO_ESP },
  { "ah", IPPROTO_AH },
};

static void print_proto(u_int16_t proto, int invert)
{
  if (proto) {
    unsigned int i;
    const char *invertstr = invert ? "! " : "";

    for (i = 0; i < sizeof(chain_protos)/sizeof(struct pprot); i++)
      if (chain_protos[i].num == proto) {
        printf("-p %s%s ",
               invertstr, chain_protos[i].name);
        return;
      }

    printf("-p %s%u ", invertstr, proto);
  }
}

static int print_match(const struct ipt_entry_match *e,
      const struct ipt_ip *ip)
{
  struct iptables_match *match
    = find_match(e->u.user.name, TRY_LOAD);

  if (match) {
    printf("-m %s ", e->u.user.name);

    /* some matches don't provide a save function */
    if (match->save)
      match->save(ip, e);
  } else {
    if (e->u.match_size) {
      fprintf(stderr,
        "Can't find library for match `%s'\n",
        e->u.user.name);
      exit(1);
    }
  }
  return 0;
}

/* print a given ip including mask if neccessary */
static void print_ip(char *prefix, u_int32_t ip, u_int32_t mask, int invert)
{
  if (!mask && !ip)
    return;

  printf("%s %s%u.%u.%u.%u",
    prefix,
    invert ? "! " : "",
    IP_PARTS(ip));

  if (mask != 0xffffffff) 
    printf("/%u.%u.%u.%u ", IP_PARTS(mask));
  else
    printf(" ");
}

/* We want this to be readable, so only print out neccessary fields.
 * Because that's the kind of world I want to live in.  */
static void print_rule(const struct ipt_entry *e, 
    iptc_handle_t *h, const char *chain, int counters)
{
  struct ipt_entry_target *t;
  const char *target_name;

  /* print counters */
  if (counters)
    printf("[%llu:%llu] ", e->counters.pcnt, e->counters.bcnt);

  /* print chain name */
  printf("-A %s ", chain);

  /* Print IP part. */
  print_ip("-s", e->ip.src.s_addr,e->ip.smsk.s_addr,
      e->ip.invflags & IPT_INV_SRCIP);  

  print_ip("-d", e->ip.dst.s_addr, e->ip.dmsk.s_addr,
      e->ip.invflags & IPT_INV_DSTIP);

  print_iface('i', e->ip.iniface, e->ip.iniface_mask,
        e->ip.invflags & IPT_INV_VIA_IN);

  print_iface('o', e->ip.outiface, e->ip.outiface_mask,
        e->ip.invflags & IPT_INV_VIA_OUT);

  print_proto(e->ip.proto, e->ip.invflags & IPT_INV_PROTO);

  if (e->ip.flags & IPT_F_FRAG)
    printf("%s-f ",
           e->ip.invflags & IPT_INV_FRAG ? "! " : "");

  /* Print matchinfo part */
  if (e->target_offset) {
    IPT_MATCH_ITERATE(e, print_match, &e->ip);
  }

  /* Print target name */  
  target_name = iptc_get_target(e, h);
  if (target_name && (*target_name != '\0'))
    printf("-j %s ", target_name);

  /* Print targinfo part */
  t = ipt_get_target((struct ipt_entry *)e);
  if (t->u.user.name[0]) {
    struct iptables_target *target
      = find_target(t->u.user.name, TRY_LOAD);

    if (!target) {
      fprintf(stderr, "Can't find library for target `%s'\n",
        t->u.user.name);
      exit(1);
    }

    if (target->save)
      target->save(&e->ip, t);
    else {
      /* If the target size is greater than ipt_entry_target
       * there is something to be saved, we just don't know
       * how to print it */
      if (t->u.target_size != 
          sizeof(struct ipt_entry_target)) {
        fprintf(stderr, "Target `%s' is missing "
            "save function\n",
          t->u.user.name);
        exit(1);
      }
    }
  }
  printf("\n");
}
/* Here ends some of the code taken from iptables-save.c ****************** */

int main(void)
{
  iptc_handle_t h;
  const struct ipt_entry *e;
  const char *chain = NULL;
  const char *tablename = "filter";
  const int counters = 1;

  program_name = "p2";
  program_version = NETFILTER_VERSION;

  /* initialize */
  h = iptc_init(tablename);
  if ( !h )   {
     printf("Error initializing: %s\n", iptc_strerror(errno));
    exit(errno);
  }

  /* print chains and their rules */
  for (chain = iptc_first_chain(&h); chain; chain = iptc_next_chain(&h))  {
    printf("%s\n", chain);
    for (e = iptc_first_rule(chain, &h); e; e = iptc_next_rule(e, &h))  {
      print_rule(e, &h, chain, counters);
    }
  }

  exit(0);

} /* main */

从以下位置借用的函数 print_ruleiptables-save.c使用以下方法以可读形式打印有关规则的信息

main 中,我们遍历每个链,并为每个链遍历每个规则并打印它。

print_rule 的参数是

好的,编译并运行程序 p2

bash# ./ipt-cc p2
bash# ./p2

您将得到

INPUT
FORWARD
OUTPUT
chain_1
chain_2

现在使用 iptables 修改环境以添加一些规则

bash# iptables -A INPUT -p tcp -i eth0 -s ! 192.168.1.1 --dport 20 -j ACCEPT
bash# iptables -A chain_1 -p udp -o eth1 -s 192.168.2.0/24 --sport 33 -j DROP

现在,如果您再次运行 p2,您将得到

INPUT
[0:0] -A INPUT -s ! 192.168.1.1 -i eth0 -p tcp -m tcp --dport 20 -j ACCEPT
FORWARD
OUTPUT
chain_1
[0:0] -A chain_1 -s 192.168.2.0/255.255.255.0 -o eth1 -p udp -m udp --sport 33 -j DROP
chain_2

我们现在为 INPUTchain_1 链打印了规则。左侧括号中的数字分别是数据包和字节计数器。

11.10. iptc_get_policy

名称: iptc_get_policy

用法: 获取给定内置链的策略。

原型: const char *iptc_get_policy(const char *chain, struct ipt_counters *counter, iptc_handle_t *handle)

描述: 此函数获取内置链的策略,并使用该策略的命中统计信息填充 counters 参数。

参数: 您必须将要获取策略的内置链的名称、指向要由函数填充的 ipt_counters 结构的指针以及标识我们正在处理的表的 iptc_handle_t 结构作为参数传递。ipt_counters 结构在上一节中已说明;不要忘记 iptc_handle_t 必须通过先前调用函数 iptc_init 获得。

返回值: 返回指向策略名称的字符指针。

使用程序 1 和程序 2 的代码片段,我们可以编写程序 #3

/* 
 * How to use libiptc- program #3
 * /usr/local/src/p3.c
 */

#include <getopt.h>
#include <sys/errno.h>
#include <stdio.h>
#include <fcntl.h>
#include <stdlib.h>
#include <string.h>
#include <dlfcn.h>
#include <time.h>
#include "libiptc/libiptc.h"
#include "iptables.h"

int main(void)
{
  iptc_handle_t h;
  const char *chain = NULL;
  const char *policy = NULL;
  const char *tablename = "filter";
  struct ipt_counters counters;

  program_name = "p3";
  program_version = NETFILTER_VERSION;

  /* initialize */
  h = iptc_init(tablename);
  if ( !h )   {
     printf("Error initializing: %s\n", iptc_strerror(errno));
    exit(errno);
  }

  /* print built-in chains, their policies and counters */
  printf("BUILT-IN   POLICY  PKTS-BYTES\n");
  printf("-----------------------------\n");
  for (chain = iptc_first_chain(&h); chain; chain = iptc_next_chain(&h))  {
    if ( !iptc_builtin(chain, h) )
      continue;
    if ( (policy = iptc_get_policy(chain, &counters, &h)) )
      printf("%-10s %-10s [%llu:%llu]\n", 
             chain, policy, counters.pcnt, counters.bcnt);
  }

  exit(0);

} /* main */

好的,编译并运行程序 p3

bash# ./ipt-cc p3
bash# ./p3

您将得到类似这样的结果

BUILT-IN  POLICY  PKTS-BYTES
----------------------------
INPUT     ACCEPT     [0:0]
FORWARD   ACCEPT     [0:0]
OUTPUT    ACCEPT     [0:0]

11.11. iptc_read_counter

名称: iptc_read_counter

用法: 读取链中规则的计数器。

原型: struct ipt_counters *iptc_read_counter(const ipt_chainlabel chain, unsigned int rulenum, iptc_handle_t *handle);

描述: 此函数读取并返回链 chain 中位于 rulenum 位置的条目规则的数据包和字节计数器。计数器在指向 ipt_counters 类型结构的指针中返回。规则编号从第一个规则的 1 开始。

参数: chain 是指向要读取的链的名称的字符指针;rulenum 是一个整数值,定义要读取其计数器的规则在规则链中的位置。handle 是指向 iptc_handle_t 类型结构的指针,该指针是通过先前调用 iptc_init 获得的。

返回值: 返回指向 ipt_counters 结构的指针,该结构包含读取的字节和数据包计数器。