iptables不能初始化filter表问题

错误如下所示:

# iptables -L
iptables v1.8.1 (legacy): can't initialize iptables table `filter': No chain/target/match by that name
Perhaps iptables or your kernel needs to be upgraded.

参考网上的说法,应该是filter表模块iptable_filter没有加载成功,或者内核编译选项根本就没有选上此功能。但是手动加载filter模块,没有问题:

# modprobe iptable_filter
# lsmod
Module                  Size  Used by
iptable_filter         16384  0
ip_tables              28672  1 iptable_filter
x_tables               40960  2 iptable_filter,ip_tables

但是问题还是存在,查一下iptables-1.8.1的源码中,打印错误的具体位置:

int do_command4(int argc, char *argv[], char **table, struct xtc_handle **handle, bool restore)
{
    ...
    /* only allocate handle if we weren't called with a handle */
    if (!*handle)
        *handle = iptc_init(*table);
       
    /* try to insmod the module if iptc_init failed */
    if (!*handle && xtables_load_ko(xtables_modprobe_program, false) != -1)
        *handle = iptc_init(*table);
   
    if (!*handle)
        xtables_error(VERSION_PROBLEM,
               "can't initialize iptables table `%s': %s",
               *table, iptc_strerror(errno));

以上可见,问题出在iptc_init函数没有正确的初始化xtc_handle类型的变量handle,如下iptc_init函数:

struct xtc_handle * TC_INIT(const char *tablename)
{
    ...
    sockfd = socket(TC_AF, SOCK_RAW, IPPROTO_RAW);
    if (sockfd < 0) return NULL;

    if (fcntl(sockfd, F_SETFD, FD_CLOEXEC) == -1) {
        fprintf(stderr, "Could not set close on exec: %s\n", strerror(errno));
        abort();
    }
    s = sizeof(info);
    strcpy(info.name, tablename);
    if (getsockopt(sockfd, TC_IPPROTO, SO_GET_INFO, &info, &s) < 0) {
        close(sockfd);
        return NULL;
    }
    DEBUGP("valid_hooks=0x%08x, num_entries=%u, size=%u\n",
        info.valid_hooks, info.num_entries, info.size);

    h = alloc_handle(&info);
    if (h == NULL) {
        close(sockfd);
        return NULL;
    }
    /* Initialize current state */
    h->sockfd = sockfd;
    h->info = info;
    h->entries->size = h->info.size;
    tmp = sizeof(STRUCT_GET_ENTRIES) + h->info.size;
    if (getsockopt(h->sockfd, TC_IPPROTO, SO_GET_ENTRIES, h->entries, &tmp) < 0)
        goto error;

最有可能出错的地方就是两个getsockopt函数,在TC_INIT函数中增加打印信息,重新编译一个iptables程序,但是TC_INIT位于libiptc/libiptc.c文件中,其编译之后为共享库文件libip4tc.so.0.1.0,将其替换系统目录/lib64下面的同名文件,方可看到打印输出。

最终确定getsocketopt函数执行SO_GET_INFO时出错,由于创建的为SOCK_RAW类型的套接口,内核处理函数为:

static int raw_getsockopt(struct sock *sk, int level, int optname,
              char __user *optval, int __user *optlen)
{
    if (level != SOL_RAW)
        return ip_getsockopt(sk, level, optname, optval, optlen);
    return do_raw_getsockopt(sk, level, optname, optval, optlen);
}

iptables中定义的level为TC_IPPROTO,实际上等于内核中的IPPROTO_IP,不等于SOL_RAW,调用函数ip_getsockopt:

int ip_getsockopt(struct sock *sk, int level,
          int optname, char __user *optval, int __user *optlen)
{
    int err;

    err = do_ip_getsockopt(sk, level, optname, optval, optlen, 0);
    ...
#ifdef CONFIG_NETFILTER
    /* we need to exclude all possible ENOPROTOOPTs except default case */
    if (err == -ENOPROTOOPT && optname != IP_PKTOPTIONS &&
            !ip_mroute_opt(optname)) {
        int len;

        if (get_user(len, optlen)) return -EFAULT;

        err = nf_getsockopt(sk, PF_INET, optname, optval, &len);
        if (err >= 0)
            err = put_user(len, optlen);
        return err;
    }
#endif

对于函数ip_getsockopt,这里有个优先级,其先处理其它系统的getsockopt调用;之后再处理netfilter的调用,即函数nf_getsockopt。在内核中增加打印信息,发现SO_GET_INFO调用并没有进入nf_getsockopt函数处理,经检查发现,之前修改内核代码时在do_ip_getsockopt中增加了和SO_GET_INFO相同的选项值,导致此错误的发生,修正之后,iptables运行正常。

相关推荐
©️2020 CSDN 皮肤主题: 编程工作室 设计师:CSDN官方博客 返回首页