just_person / 待分类 / iptc library HOWTO



iptc library HOWTO

2019-09-19  just_pers...

iptc library HOWTO


The old fashion netfilter rules creation by iptables from the shell is not working for you? Do you looking for you own, programmatic way to create those rule? You have the iptc library that makes this process possible. The libiptc composed of structs which represent a netfilter rules, and functions used to submit those rules. Structs are the focus of this howto.


This document is Copyright © 2010 Grigoriy Eykalis. Permission is granted to copy, distribute and/or modify this document under the terms of the GNU Free Documentation License, Version 1.1 or any later version published by the Free Software Foundation with no Invariant Sections, no Front-Cover Texts, and no Back-Cover Texts.


Use the information in this document at your own risk. I disavow any potential liability for the contents of this document. Use of the concepts, examples, and/or other content of this document is entirely at your own risk. All copyrights are owned by their owners, unless specifically noted otherwise. Use of a term in this document should not be regarded as affecting the validity of any trademark or service mark. Naming of particular products or brands should not be seen as endorsements. You are strongly recommended to take a backup of your system before major installation and backups at regular intervals.


If you want to translate this document you are free to do so. I'll really appreciate if you will let me know about this.


Hi folks. This document aims to be usable for people, which build any kind firewalls/content filters on Linux 2.6 OS. It is assumed you know what the netfilter and iptables are and how they work. At least, you should know how to create a simple netfilter rule using the iptables. Additionally, I highly recommend to get familiar with the guide first.

Sometimes you need to change the Netfilter rules from the C/C++ code. But there is no official API for this purpose, except the shell style iptables. So you need to start hacking inside the Netfilter. IPTC library could be extremely helpful, with one point to remember: this is an internal library (for example it is used by iptables) and there is no guarantee, even in GNU license terms, that any changes will be done in next version of the Netfilter. But, if you believe in the stability and constancy of the temporary decisions, this manual is for you. Anyway this manual and the sample code were tested on: Linux 2.6.24-24-generic #1 SMP Tue Jul 7 19:46:39 UTC 2009 i686 GNU/Linux.


Using iptc, the Netfilter rule should be represented by chain of structs in sequential order in the memory:

|<---struct ipt_entry--->|<---struct ipt_entry_match + struct ipt/xt_someModule---> (can presence many times)|<---struct ipt_entry_target + struct ipt/xt_someTarget--->(only one)|

ipt_entry defines common parameters of the rule, like source/destination ip's, and the layout of the whole memory chunk, where chain of those structs located.

Let's see ipt_entry struct (my comments are inside the code):

struct ipt_entry
        struct ipt_ip ip;//source and destination IP address, mask for source and
                             //destination IP address, interface alias, which protocol
                             //should be used, etc

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

        /* Size of ipt_entry + matches */
        u_int16_t target_offset;//where the target struct begins – after the match
                                        //struct/s or right after the ipt_entry struct
        /* Size of ipt_entry + matches + target */
        u_int16_t next_offset;//where the next ipt_entry struct is

        /* Back pointer */
        unsigned int comefrom;

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

        /* The matches (if any), then the target. */
        unsigned char elems[0];//a pointer to the next match/target struct (next step in memory)

One rule (one structs chain) can contain several numbers of matches, which represent Netfilter modules. The match is composed of ipt_entry_match and ipt/xt_'module' structs. Even if you don't know what is the module name (for specific case), you can find it by google "match extension for someModule”.

And now ipt_entry_match struct:

#define ipt_entry_match xt_entry_match

struct xt_entry_match
        union {
                struct {
                        u_int16_t match_size;

                        /* Used by userspace */
                        char name[XT_FUNCTION_MAXNAMELEN-1];

                        u_int8_t revision;
                } user;
                struct {
                        u_int16_t match_size;

                        /* Used inside the kernel */
                        struct xt_match *match;
                } kernel;

                /* Total length */
                u_int16_t match_size;
        } u;

        unsigned char data[0];

The ipt/xt_'module' struct can vary for different cases. So let's take for example ipt_udp struct:

#define ipt_udp xt_udp

struct xt_udp
        u_int16_t spts[2];                      /* Source port range. */
        u_int16_t dpts[2];                      /* Destination port range. */
        u_int8_t invflags;                      /* Inverse flags */

Nothing complicated.

Each rule (one structs chain) contains one target only. Similar to matches, the target consists of ipt_entry_target, with or without ipt/xt_'target' structs.


#define ipt_entry_target xt_entry_target

struct xt_entry_target
        union {
                struct {
                        u_int16_t target_size;

                        /* Used by userspace */
                        char name[XT_FUNCTION_MAXNAMELEN-1];

                        u_int8_t revision;
                } user;
                struct {
                        u_int16_t target_size;

                        /* Used inside the kernel */
                        struct xt_target *target;
                } kernel;

                /* Total length */
                u_int16_t target_size;
        } u;

        unsigned char data[0];

For NFQUEUE the xt_NFQ_info struct should be used:

/* target info */
struct xt_NFQ_info {
        u_int16_t queuenum;


And now let's see the small example (the working one). We'd like to create iptables' equivalent rule:

iptables -A INPUT -s -d -i eth0 -p tcp --sport 0:59136 --dport 0:51201 -m limit -limit 2000/s --limit-burst 10 -m physdev-in eth0 -j ACCEPT


 * To compile this code, use the following line:
 * gcc -g -o test test.c -liptc -liptables -ldl

#include <sys/errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "libiptc/libiptc.h"
#include "linux/netfilter/xt_limit.h"
#include "linux/netfilter/xt_physdev.h"
#include "iptables.h"
#include <netinet/in.h>

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

        struct ipt_entry * e;
        struct ipt_entry_match * match_proto, * match_limit, * match_physdev;
        struct ipt_entry_target * target;
        struct ipt_tcp * tcpinfo;
        struct xt_rateinfo * rateinfo;
        struct xt_physdev_info * physdevinfo;
        unsigned int size_ipt_entry, size_ipt_entry_match, size_ipt_entry_target, size_ipt_tcp, size_rateinfo, size_physdevinfo, total_length;

        size_ipt_entry = IPT_ALIGN(sizeof(struct ipt_entry));
        size_ipt_entry_match = IPT_ALIGN(sizeof(struct ipt_entry_match));
        size_ipt_entry_target = 36;//IPT_ALIGN(sizeof(struct ipt_entry_target)); I don't know why the IPT_ALIGN() does not work in this case, may be 36 is 42 in netfilter :)
        size_ipt_tcp = IPT_ALIGN(sizeof(struct ipt_tcp));
        size_rateinfo = IPT_ALIGN(sizeof(struct xt_rateinfo));
        size_physdevinfo = IPT_ALIGN(sizeof(struct xt_physdev_info));
        total_length =  size_ipt_entry + size_ipt_entry_match * 3 + size_ipt_entry_target + size_ipt_tcp + size_rateinfo + size_physdevinfo;

        //memory allocation for all structs that represent the netfilter rule we want to insert
        e = calloc(1, total_length);
        if(e == NULL)
                printf("malloc failure");

        //offsets to the other bits:
        //target struct begining
        e->target_offset = size_ipt_entry + size_ipt_entry_match * 3 + size_ipt_tcp + size_rateinfo + size_physdevinfo;
        //next "e" struct, end of the current one
        e->next_offset = total_length;

        //set up packet matching rules: “-s -d -i eth0” part
        //of our desirable rule
        e->ip.src.s_addr = inet_addr("");
        e->ip.smsk.s_addr= inet_addr("");
        e->ip.dst.s_addr = inet_addr("");
        e->ip.dmsk.s_addr= inet_addr("");
        e->ip.proto = IPPROTO_TCP;
        e->nfcache = 0;
        strcpy(e->ip.iniface, "eth0");

        //match structs setting:
        //set match rule for the protocol to use
        //”-p tcp” part of our desirable rule
        match_proto = (struct ipt_entry_match *) e->elems;
        match_proto->u.match_size = size_ipt_entry_match + size_ipt_tcp;
        strcpy(match_proto->u.user.name, "tcp");//set name of the module, we will use in this match

        //set match rule for the packet number per time limitation - against DoS attacks
        //”-m limit” part of our desirable rule
        match_limit = (struct ipt_entry_match *) (e->elems + match_proto->u.match_size);
        match_limit->u.match_size = size_ipt_entry_match + size_rateinfo;
        strcpy(match_limit->u.user.name, "limit");//set name of the module, we will use in this match

        //set match rule for specific Ethernet card (interface)
        //”-m physdev” part of our desirable rule
        match_physdev = (struct ipt_entry_match *) (e->elems + match_proto->u.match_size + match_limit->u.match_size);
        match_physdev->u.match_size = size_ipt_entry_match + size_physdevinfo;
        strcpy(match_physdev->u.user.name, "physdev");//set name of the module, we will use in this match

        //tcp module - match extension
        //”--sport 0:59136 --dport 0:51201” part of our desirable rule
        tcpinfo = (struct ipt_tcp *)match_proto->data;
        tcpinfo->spts[0] = ntohs(0);
        tcpinfo->spts[1] = ntohs(0xE7);
        tcpinfo->dpts[0] = ntohs(0);
        tcpinfo->dpts[1] = ntohs(0x1C8);

        //limit module - match extension
        //”-limit 2000/s --limit-burst 10” part of our desirable rule
        rateinfo = (struct xt_rateinfo *)match_limit->data;
        rateinfo->avg = 5;
        rateinfo->burst = 10;

        //physdev module - match extension
        //”-in eth0” part of our desirable rule
        physdevinfo = (struct xt_physdev_info *)match_physdev->data;
        strcpy(physdevinfo->physindev, "eth0");
        memset(physdevinfo->in_mask, 0xFF, IFNAMSIZ);
        physdevinfo->bitmask = 1;

        //target struct
        //”-j ACCEPT” part of our desirable rule
        target = (struct ipt_entry_target *)(e->elems + size_ipt_entry_match * 3 + size_ipt_tcp + size_rateinfo + size_physdevinfo);
        target->u.target_size = size_ipt_entry_target;
        strcpy(target->u.user.name, "ACCEPT");

        program_name = "p4";
        program_version = "NETFILTER_VERSION";

        //All the functions, mentioned below could be found in "Querying libiptc HOWTO" manual
        h = iptc_init(tablename);
        if ( !h )
                printf("Error initializing: %s\n", iptc_strerror(errno));

        //analogous to “iptables -A INPUT” part of our desirable rule + the rule itself         
        //inside of the e struct
        int x = iptc_append_entry(chain, e, &h);
        if (!x)
                printf("Error append_entry: %s\n", iptc_strerror(errno));
        printf("%s", target->data);
        int y = iptc_commit(&h);
        if (!y)
                printf("Error commit: %s\n", iptc_strerror(errno));






    请遵守用户 评论公约

    类似文章 更多
    喜欢该文的人也喜欢 更多