文章目录

  • 框架
  • 数据结构
  • ebtables_init
  • translate_table
  • find_table_lock
  • 过滤配置表注册
  • 过滤配置规则的注册
    • 数据结构
    • ip过滤匹配
  • ebt_do_table
  • EBT_MATCH_ITERATE
  • ebt_do_match
  • ebt_ip_mt

继续上篇文章 《linux etables tool》,看一下kernel中ebtables 框架。
代码目录:\net\bridge\netfilter

框架

ebtables core负责hook机制的实现,并且向上提供socket接口,供用户空间配置和查询过滤配置选项,向下提供注册接口,供各个table,规则,动作等过滤配置的注册。

数据结构

struct ebt_replace为用户空间调用socket接口配置内核时接口参数结构,描述了用户配置的过滤配置。

struct ebt_replace {char name[EBT_TABLE_MAXNAMELEN];unsigned int valid_hooks;/* nr of rules in the table */unsigned int nentries;/* total size of the entries */unsigned int entries_size;/* start of the chains */struct ebt_entries __user *hook_entry[NF_BR_NUMHOOKS];//指向每个链上规则首指针/* nr of counters userspace expects back */unsigned int num_counters;/* where the kernel will put the old counters */struct ebt_counter __user *counters;char __user *entries;//每个链上规则,可以多个规则,上面hook_entry[]指向每个链上第一个规则
};

struct ebt_table_info 是kernel中存储用户过滤配置的结构,就是把上面ebt_replace转换为ebt_table_info 中存储

struct ebt_table_info {/* total size of the entries */unsigned int entries_size;unsigned int nentries;/* pointers to the start of the chains */struct ebt_entries *hook_entry[NF_BR_NUMHOOKS];//指向每个链上规则首指针/* room to maintain the stack used for jumping from and into udc */struct ebt_chainstack **chainstack;char *entries;//每个链上规则,可以多个规则,上面hook_entry[]指向每个链上第一个规则struct ebt_counter counters[0] ____cacheline_aligned;
};

struct ebt_table为过滤配置表,有broute,nat,filter三种,其中的struct ebt_table_info *private;指向了上面的过滤配置信息

struct ebt_table {struct list_head list;char name[EBT_TABLE_MAXNAMELEN];struct ebt_replace_kernel *table;unsigned int valid_hooks;rwlock_t lock;/* e.g. could be the table explicitly only allows certain* matches, targets, ... 0 == let it in */int (*check)(const struct ebt_table_info *info,unsigned int valid_hooks);/* the data used by the kernel */struct ebt_table_info *private;struct module *me;
};

struct ebt_entries 为ebt_table_info 中char *entries;每个链上规则,可以多个规则,

struct ebt_entries {/* this field is always set to zero* See EBT_ENTRY_OR_ENTRIES.* Must be same size as ebt_entry.bitmask */unsigned int distinguisher;/* the chain name */char name[EBT_CHAIN_MAXNAMELEN];/* counter offset for this chain */unsigned int counter_offset;/* one standard (accept, drop, return) per hook */int policy;/* nr. of entries */unsigned int nentries;/* entry list */char data[0] __attribute__ ((aligned (__alignof__(struct ebt_replace))));
};

struct ebt_entry为ebt_entries 中char data[0];指向具体匹配规则选项,可以有多个匹配规则
上面hook_entry[]指向每个链上第一个规则

/* one entry */
struct ebt_entry {/* this needs to be the first field */unsigned int bitmask;unsigned int invflags;__be16 ethproto;/* the physical in-dev */char in[IFNAMSIZ];/* the logical in-dev */char logical_in[IFNAMSIZ];/* the physical out-dev */char out[IFNAMSIZ];/* the logical out-dev */char logical_out[IFNAMSIZ];unsigned char sourcemac[ETH_ALEN];unsigned char sourcemsk[ETH_ALEN];unsigned char destmac[ETH_ALEN];unsigned char destmsk[ETH_ALEN];/* sizeof ebt_entry + matches */unsigned int watchers_offset; 跨过所有的ebt_entry_match指向第一个ebt_entry_watcher/* sizeof ebt_entry + matches + watchers */unsigned int target_offset;跨过所有的ebt_entry_match,ebt_entry_watcher指向第一个ebt_entry_target/* sizeof ebt_entry + matches + watchers + target */unsigned int next_offset; 指向下一个ebt_entry unsigned char elems[0] __attribute__ ((aligned (__alignof__(struct ebt_replace))));// elems[0]指向ebt_entry_match
};

ebt_entry_match中data指向具体过滤配置参数,例如,ip为struct ebt_ip_info

ebt_match ,ebt_target 描述过滤配置匹配规则和执行的动作

struct ebt_match {struct list_head list;const char name[EBT_FUNCTION_MAXNAMELEN];bool (*match)(const struct sk_buff *skb, const struct net_device *in,const struct net_device *out, const struct xt_match *match,const void *matchinfo, int offset, unsigned int protoff,bool *hotdrop);bool (*checkentry)(const char *table, const void *entry,const struct xt_match *match, void *matchinfo,unsigned int hook_mask);void (*destroy)(const struct xt_match *match, void *matchinfo);unsigned int matchsize;u_int8_t revision;u_int8_t family;struct module *me;
};struct ebt_watcher {struct list_head list;const char name[EBT_FUNCTION_MAXNAMELEN];unsigned int (*target)(struct sk_buff *skb,const struct net_device *in, const struct net_device *out,unsigned int hook_num, const struct xt_target *target,const void *targinfo);bool (*checkentry)(const char *table, const void *entry,const struct xt_target *target, void *targinfo,unsigned int hook_mask);void (*destroy)(const struct xt_target *target, void *targinfo);unsigned int targetsize;u_int8_t revision;u_int8_t family;struct module *me;
};struct ebt_target {struct list_head list;const char name[EBT_FUNCTION_MAXNAMELEN];/* returns one of the standard EBT_* verdicts */unsigned int (*target)(struct sk_buff *skb,const struct net_device *in, const struct net_device *out,unsigned int hook_num, const struct xt_target *target,const void *targinfo);bool (*checkentry)(const char *table, const void *entry,const struct xt_target *target, void *targinfo,unsigned int hook_mask);void (*destroy)(const struct xt_target *target, void *targinfo);unsigned int targetsize;u_int8_t revision;u_int8_t family;struct module *me;
};

ebtables_init

ebtables初始化,ebtables.c

static int __init ebtables_init(void)
{int ret;ret = xt_register_target(&ebt_standard_target);if (ret < 0)return ret;#def 注册socket,提供给user空间的socket接口ret = nf_register_sockopt(&ebt_sockopts); if (ret < 0) {xt_unregister_target(&ebt_standard_target);return ret;}printk(KERN_INFO "Ebtables v2.0 registered\n");return 0;
}static struct nf_sockopt_ops ebt_sockopts =
{.pf        = PF_INET,.set_optmin  = EBT_BASE_CTL,  //能够处理的set 选项索引最小值.set_optmax = EBT_SO_SET_MAX + 1,//能够处理的set 选项索引最大值.set       = do_ebt_set_ctl,
#ifdef CONFIG_COMPAT.compat_set = compat_do_ebt_set_ctl,
#endif.get_optmin   = EBT_BASE_CTL,.get_optmax = EBT_SO_GET_MAX + 1,.get     = do_ebt_get_ctl,
#ifdef CONFIG_COMPAT.compat_get = compat_do_ebt_get_ctl,
#endif.owner        = THIS_MODULE,
};

用户空间ebtables tool调用
sockfd = socket(AF_INET, SOCK_RAW, PF_INET); 创建SOCK_RAW
setsockopt(sockfd, IPPROTO_IP, EBT_SO_SET_ENTRIES, repl, optlen) 通过EBT_SO_SET_ENTRIES把过滤配置传递给kernel。
对应调用

\net\ipv4\raw.c
raw_setsockopt
\net\ipv4\ip_sockglue.c
ip_setsockopt
nf_sockopt.c
nf_setsockopt
nf_sockopt
do_ebt_set_ctlstatic int do_ebt_set_ctl(struct sock *sk,int cmd, void __user *user, unsigned int len)
{int ret;struct net *net = sock_net(sk);if (!ns_capable(net->user_ns, CAP_NET_ADMIN))return -EPERM;switch(cmd) {#def 找到了EBT_SO_SET_ENTRIES 配置地方case EBT_SO_SET_ENTRIES:ret = do_replace(net, user, len);break;case EBT_SO_SET_COUNTERS:ret = update_counters(net, user, len);break;default:ret = -EINVAL;}return ret;
}

do_replace把用户空间过滤配置存储到kernel

/* replace the table */
static int do_replace(struct net *net, const void __user *user,unsigned int len)
{int ret, countersize;struct ebt_table_info *newinfo;struct ebt_replace tmp;#def 先把用户空间数据copy到kernel空间if (copy_from_user(&tmp, user, sizeof(tmp)) != 0)return -EFAULT;tmp.name[sizeof(tmp.name) - 1] = 0;countersize = COUNTER_OFFSET(tmp.nentries) * nr_cpu_ids;#def 申请ebt_table_info 结构存储空间,用于存储过滤配置newinfo = vmalloc(sizeof(*newinfo) + countersize);if (!newinfo)return -ENOMEM;if (countersize)memset(newinfo->counters, 0, countersize);newinfo->entries = vmalloc(tmp.entries_size);#def 复制到ebt_table_info  表中规则和执行动作if (copy_from_user(newinfo->entries, tmp.entries, tmp.entries_size) != 0) {BUGPRINT("Couldn't copy entries from userspace\n");ret = -EFAULT;goto free_entries;}ret = do_replace_finish(net, &tmp, newinfo);}
static int do_replace_finish(struct net *net, struct ebt_replace *repl,struct ebt_table_info *newinfo)
{int ret, i;struct ebt_counter *counterstmp = NULL;/* used to be able to unlock earlier */struct ebt_table_info *table;struct ebt_table *t;/* the user wants counters backthe check on the size is done later, when we have the lock */if (repl->num_counters) {unsigned long size = repl->num_counters * sizeof(*counterstmp);counterstmp = vmalloc(size);if (!counterstmp)return -ENOMEM;}newinfo->chainstack = NULL;ret = ebt_verify_pointers(repl, newinfo);if (ret != 0)goto free_counterstmp;//用户空间和kernel配置表信息转换ret = translate_table(net, repl->name, newinfo);if (ret != 0)goto free_counterstmp;#def 根据name找到table,比如broute,nat,filter表等t = find_table_lock(net, repl->name, &ret, &ebt_mutex);if (!t) {ret = -ENOENT;goto free_iterate;}/* the table doesn't like it */if (t->check && (ret = t->check(newinfo, repl->valid_hooks)))goto free_unlock;if (repl->num_counters && repl->num_counters != t->private->nentries) {BUGPRINT("Wrong nr. of counters requested\n");ret = -EINVAL;goto free_unlock;}/* we have the mutex lock, so no danger in reading this pointer */table = t->private;/* make sure the table can only be rmmod'ed if it contains no rules */if (!table->nentries && newinfo->nentries && !try_module_get(t->me)) {ret = -ENOENT;goto free_unlock;} else if (table->nentries && !newinfo->nentries)module_put(t->me);/* we need an atomic snapshot of the counters */write_lock_bh(&t->lock);if (repl->num_counters)get_counters(t->private->counters, counterstmp,t->private->nentries);#def 存储到private 中t->private = newinfo;write_unlock_bh(&t->lock);mutex_unlock(&ebt_mutex);/* so, a user can change the chains while having messed up her counterallocation. Only reason why this is done is because this way the lockis held only once, while this doesn't bring the kernel into adangerous state. */if (repl->num_counters &&copy_to_user(repl->counters, counterstmp,repl->num_counters * sizeof(struct ebt_counter))) {/* Silent error, can't fail, new table is already in place */net_warn_ratelimited("ebtables: counters copy to user failed while replacing table\n");}/* decrease module count and free resources */EBT_ENTRY_ITERATE(table->entries, table->entries_size,ebt_cleanup_entry, net, NULL);vfree(table->entries);if (table->chainstack) {for_each_possible_cpu(i)vfree(table->chainstack[i]);vfree(table->chainstack);}vfree(table);vfree(counterstmp);return ret;free_unlock:mutex_unlock(&ebt_mutex);
free_iterate:EBT_ENTRY_ITERATE(newinfo->entries, newinfo->entries_size,ebt_cleanup_entry, net, NULL);
free_counterstmp:vfree(counterstmp);/* can be initialized in translate_table() */if (newinfo->chainstack) {for_each_possible_cpu(i)vfree(newinfo->chainstack[i]);vfree(newinfo->chainstack);}return ret;
}

translate_table

static int translate_table(struct net *net, const char *name,struct ebt_table_info *newinfo)
{ret = EBT_ENTRY_ITERATE(newinfo->entries, newinfo->entries_size,ebt_check_entry, net, newinfo, name, &i, cl_s, udc_cnt);
}ebt_check_entry--》ebt_check_matchmatch = xt_request_find_match(NFPROTO_BRIDGE, m->u.name, 0);//从全局变量xt[af].match链表中查找,后面介绍如何注册的m->u.match = match; //赋值到m->u.match,也就是struct ebt_entry_match结构中u.match,供匹配时使用par->match     = match;par->matchinfo = m->data;ebt_check_entry--》ebt_check_watcherwatcher = xt_request_find_target(NFPROTO_BRIDGE, w->u.name, 0);//从全局变量xt[af].watcher 链表中查找,后面介绍如何注册的w->u.watcher = watcher;//赋值到m->u.match,也就是struct ebt_entry_watcher结构中u.watcher,供匹配时使用par->target   = watcher;par->targinfo = w->data;ebt_check_entry--》target = xt_request_find_target(NFPROTO_BRIDGE, t->u.name, 0);//从全局变量xt[af].target链表中查找,后面介绍如何注册的t->u.target = target;//赋值到t->u.target,也就是struct ebt_entry_target结构中u.watcher,供匹配时执行动作使用上面复用了union变量,name和match 或watcher或target,用户空间传进来为name,kernel根据name查找到后,赋值match 或watcher或target

find_table_lock

static inline struct ebt_table *
find_table_lock(struct net *net, const char *name, int *error,struct mutex *mutex)
{#def 从net->xt.tables[NFPROTO_BRIDGE]查找表ebt_table return find_inlist_lock(&net->xt.tables[NFPROTO_BRIDGE], name,"ebtable_", error, mutex);
}

过滤配置表注册

ebt_register_table
kernel中过滤配置表通过ebt_register_table注册到net->xt.tables[NFPROTO_BRIDGE] 链表中

struct ebt_table *
ebt_register_table(struct net *net, const struct ebt_table *input_table)
{struct ebt_table_info *newinfo;struct ebt_table *t, *table;struct ebt_replace_kernel *repl;int ret, i, countersize;void *p;if (input_table == NULL || (repl = input_table->table) == NULL ||repl->entries == NULL || repl->entries_size == 0 ||repl->counters != NULL || input_table->private != NULL) {BUGPRINT("Bad table data for ebt_register_table!!!\n");return ERR_PTR(-EINVAL);}/* Don't add one table to multiple lists. */table = kmemdup(input_table, sizeof(struct ebt_table), GFP_KERNEL);if (!table) {ret = -ENOMEM;goto out;}countersize = COUNTER_OFFSET(repl->nentries) * nr_cpu_ids;newinfo = vmalloc(sizeof(*newinfo) + countersize);ret = -ENOMEM;if (!newinfo)goto free_table;p = vmalloc(repl->entries_size);if (!p)goto free_newinfo;memcpy(p, repl->entries, repl->entries_size);newinfo->entries = p;newinfo->entries_size = repl->entries_size;newinfo->nentries = repl->nentries;if (countersize)memset(newinfo->counters, 0, countersize);/* fill in newinfo and parse the entries */newinfo->chainstack = NULL;for (i = 0; i < NF_BR_NUMHOOKS; i++) {if ((repl->valid_hooks & (1 << i)) == 0)newinfo->hook_entry[i] = NULL;elsenewinfo->hook_entry[i] = p +((char *)repl->hook_entry[i] - repl->entries);}ret = translate_table(net, repl->name, newinfo);if (ret != 0) {BUGPRINT("Translate_table failed\n");goto free_chainstack;}if (table->check && table->check(newinfo, table->valid_hooks)) {BUGPRINT("The table doesn't like its own initial data, lol\n");ret = -EINVAL;goto free_chainstack;}table->private = newinfo;rwlock_init(&table->lock);ret = mutex_lock_interruptible(&ebt_mutex);if (ret != 0)goto free_chainstack;list_for_each_entry(t, &net->xt.tables[NFPROTO_BRIDGE], list) {if (strcmp(t->name, table->name) == 0) {ret = -EEXIST;BUGPRINT("Table name already exists\n");goto free_unlock;}}/* Hold a reference count if the chains aren't empty */if (newinfo->nentries && !try_module_get(table->me)) {ret = -ENOENT;goto free_unlock;}#def 添加到net->xt.tables[NFPROTO_BRIDGE] 中list_add(&table->list, &net->xt.tables[NFPROTO_BRIDGE]);mutex_unlock(&ebt_mutex);return table;return ERR_PTR(ret);
}

ebtable_broute.c


static struct pernet_operations broute_net_ops = {.init = broute_net_init,.exit = broute_net_exit,
};static int __init ebtable_broute_init(void)
{int ret;#def 注册broute 操作函数ret = register_pernet_subsys(&broute_net_ops);if (ret < 0)return ret;/* see br_input.c */#def 设置br_should_route_hook指针 ebt_broute,在br_input中调用RCU_INIT_POINTER(br_should_route_hook,(br_should_route_hook_t *)ebt_broute);return 0;
}static int __net_init broute_net_init(struct net *net)
{   #def 注册broute到net->xt.tables[NFPROTO_BRIDGE] 中net->xt.broute_table = ebt_register_table(net, &broute_table);return PTR_RET(net->xt.broute_table);
}/* EBT_ACCEPT means the frame will be bridged* EBT_DROP means the frame will be routed*/#def 默认规则EBT_ACCEPT  will be bridged
static struct ebt_entries initial_chain = {.name       = "BROUTING",.policy     = EBT_ACCEPT,
};#def 初始化表,默认使用的
static struct ebt_replace_kernel initial_table =
{.name      = "broute",.valid_hooks  = 1 << NF_BR_BROUTING,.entries_size  = sizeof(struct ebt_entries),.hook_entry   = {[NF_BR_BROUTING]    = &initial_chain,},.entries    = (char *)&initial_chain,
};#def broute_table表
static const struct ebt_table broute_table =
{.name      = "broute",.table        = &initial_table, //初始化表,默认使用的.valid_hooks  = 1 << NF_BR_BROUTING, //在NF_BR_BROUTING hook上.check     = check,.me        = THIS_MODULE,
};

过滤配置规则的注册

内核中提供过滤配置规则,如ip,ipv6,arp,802.3,nat,vlan,stp等等
在目录下net\bridge\netfilter各个文件

数据结构

struct xt_match {struct list_head list;const char name[XT_EXTENSION_MAXNAMELEN];u_int8_t revision;/* Return true or false: return FALSE and set *hotdrop = 1 toforce immediate packet drop. *//* Arguments changed since 2.6.9, as this must now handlenon-linear skb, using skb_header_pointer andskb_ip_make_writable. */bool (*match)(const struct sk_buff *skb,struct xt_action_param *);/* Called when user tries to insert an entry of this type. */int (*checkentry)(const struct xt_mtchk_param *);/* Called when entry of this type deleted. */void (*destroy)(const struct xt_mtdtor_param *);
#ifdef CONFIG_COMPAT/* Called when userspace align differs from kernel space one */void (*compat_from_user)(void *dst, const void *src);int (*compat_to_user)(void __user *dst, const void *src);
#endif/* Set this to THIS_MODULE if you are a module, otherwise NULL */struct module *me;const char *table;unsigned int matchsize;
#ifdef CONFIG_COMPATunsigned int compatsize;
#endifunsigned int hooks;unsigned short proto;unsigned short family;
};/* Registration hooks for targets. */
struct xt_target {struct list_head list;const char name[XT_EXTENSION_MAXNAMELEN];u_int8_t revision;/* Returns verdict. Argument order changed since 2.6.9, as thismust now handle non-linear skbs, using skb_copy_bits andskb_ip_make_writable. */unsigned int (*target)(struct sk_buff *skb,const struct xt_action_param *);/* Called when user tries to insert an entry of this type:hook_mask is a bitmask of hooks from which it can becalled. *//* Should return 0 on success or an error code otherwise (-Exxxx). */int (*checkentry)(const struct xt_tgchk_param *);/* Called when entry of this type deleted. */void (*destroy)(const struct xt_tgdtor_param *);
#ifdef CONFIG_COMPAT/* Called when userspace align differs from kernel space one */void (*compat_from_user)(void *dst, const void *src);int (*compat_to_user)(void __user *dst, const void *src);
#endif/* Set this to THIS_MODULE if you are a module, otherwise NULL */struct module *me;const char *table;unsigned int targetsize;
#ifdef CONFIG_COMPATunsigned int compatsize;
#endifunsigned int hooks;unsigned short proto;unsigned short family;
};/* Furniture shopping... */
struct xt_table {struct list_head list;/* What hooks you will enter on */unsigned int valid_hooks;/* Man behind the curtain... */struct xt_table_info *private;/* Set this to THIS_MODULE if you are a module, otherwise NULL */struct module *me;u_int8_t af;      /* address/protocol family */int priority;      /* hook order *//* A unique name... */const char name[XT_TABLE_MAXNAMELEN];
};

ip过滤匹配

ebt_ip.c

ip的匹配规则结构
static struct xt_match ebt_ip_mt_reg __read_mostly = {.name        = "ip",.revision = 0,.family        = NFPROTO_BRIDGE,.match        = ebt_ip_mt,.checkentry    = ebt_ip_mt_check,.matchsize   = sizeof(struct ebt_ip_info),.me       = THIS_MODULE,
};static int __init ebt_ip_init(void)
{return xt_register_match(&ebt_ip_mt_reg); //注册到core中static struct xt_af *xt;链表中
}static void __exit ebt_ip_fini(void)
{xt_unregister_match(&ebt_ip_mt_reg);
}module_init(ebt_ip_init);// 注册到.init段,kernel启动过程中自动调用
module_exit(ebt_ip_fini);
int
xt_register_match(struct xt_match *match)
{u_int8_t af = match->family;int ret;ret = mutex_lock_interruptible(&xt[af].mutex);if (ret != 0)return ret;list_add(&match->list, &xt[af].match);mutex_unlock(&xt[af].mutex);return ret;
}

ebt_do_table

每个hook点调用ebt_do_table ,进行过滤。

/* Do some firewalling */
unsigned int ebt_do_table (unsigned int hook, struct sk_buff *skb,const struct net_device *in, const struct net_device *out,struct ebt_table *table)
{int i, nentries;struct ebt_entry *point;struct ebt_counter *counter_base, *cb_base;const struct ebt_entry_target *t;int verdict, sp = 0;struct ebt_chainstack *cs;struct ebt_entries *chaininfo;const char *base;const struct ebt_table_info *private;struct xt_action_param acpar;acpar.family  = NFPROTO_BRIDGE;acpar.in      = in;acpar.out     = out;acpar.hotdrop = false;acpar.hooknum = hook;read_lock_bh(&table->lock);private = table->private; //private中存储ebt_table_info ,前面已介绍cb_base = COUNTER_BASE(private->counters, private->nentries,smp_processor_id());if (private->chainstack)cs = private->chainstack[smp_processor_id()];elsecs = NULL;chaininfo = private->hook_entry[hook]; //对应hook 链信息nentries = private->hook_entry[hook]->nentries; //过滤配置规则个数point = (struct ebt_entry *)(private->hook_entry[hook]->data); //执行具体过滤规则匹配项counter_base = cb_base + private->hook_entry[hook]->counter_offset;/* base for chain jumps */base = private->entries;i = 0;while (i < nentries) {if (ebt_basic_match(point, skb, in, out))goto letscontinue;if (EBT_MATCH_ITERATE(point, ebt_do_match, skb, &acpar) != 0) //过滤规则匹配,循环查找,发现name相同,完成goto letscontinue;if (acpar.hotdrop) {read_unlock_bh(&table->lock);return NF_DROP;}/* increase counter */(*(counter_base + i)).pcnt++;(*(counter_base + i)).bcnt += skb->len;/* these should only watch: not modify, nor tell uswhat to do with the packet */EBT_WATCHER_ITERATE(point, ebt_do_watcher, skb, &acpar);//匹配watcher//前面EBT_MATCH_ITERATE没有匹配到,才走这里,先看释放上层配置了t->u.target->target,如果//没有配置,就使用默认ebt_standard_target t = (struct ebt_entry_target *)(((char *)point) + point->target_offset);/* standard target */if (!t->u.target->target)verdict = ((struct ebt_standard_target *)t)->verdict;else {acpar.target   = t->u.target;acpar.targinfo = t->data;verdict = t->u.target->target(skb, &acpar);}if (verdict == EBT_ACCEPT) { //接收read_unlock_bh(&table->lock);return NF_ACCEPT;}if (verdict == EBT_DROP) { //丢包read_unlock_bh(&table->lock);return NF_DROP;}if (verdict == EBT_RETURN) {letsreturn:
#ifdef CONFIG_NETFILTER_DEBUGif (sp == 0) {BUGPRINT("RETURN on base chain");/* act like this is EBT_CONTINUE */goto letscontinue;}
#endifsp--;/* put all the local variables right */i = cs[sp].n;chaininfo = cs[sp].chaininfo;nentries = chaininfo->nentries;point = cs[sp].e;counter_base = cb_base +chaininfo->counter_offset;continue;}if (verdict == EBT_CONTINUE)goto letscontinue;
#ifdef CONFIG_NETFILTER_DEBUGif (verdict < 0) {BUGPRINT("bogus standard verdict\n");read_unlock_bh(&table->lock);return NF_DROP;}
#endif/* jump to a udc */cs[sp].n = i + 1;cs[sp].chaininfo = chaininfo;cs[sp].e = ebt_next_entry(point);i = 0;chaininfo = (struct ebt_entries *) (base + verdict);
#ifdef CONFIG_NETFILTER_DEBUGif (chaininfo->distinguisher) {BUGPRINT("jump to non-chain\n");read_unlock_bh(&table->lock);return NF_DROP;}
#endifnentries = chaininfo->nentries;point = (struct ebt_entry *)chaininfo->data;counter_base = cb_base + chaininfo->counter_offset;sp++;continue;
letscontinue:point = ebt_next_entry(point);i++;}/* I actually like this :) *///如果上层没有配置匹配过滤规则,走hook 链默认配置if (chaininfo->policy == EBT_RETURN)goto letsreturn;if (chaininfo->policy == EBT_ACCEPT) {read_unlock_bh(&table->lock);return NF_ACCEPT;}read_unlock_bh(&table->lock);return NF_DROP;
}

EBT_MATCH_ITERATE

/* blatently stolen from ip_tables.h* fn returns 0 to continue iteration */
#define EBT_MATCH_ITERATE(e, fn, args...)                   \
({                                                          \unsigned int __i;                                   \int __ret = 0;                                      \struct ebt_entry_match *__match;                    \\for (__i = sizeof(struct ebt_entry);                \__i < (e)->watchers_offset;    //直到 watchers_offset,前面数据结构中已经介绍               \__i += __match->match_size +                   \sizeof(struct ebt_entry_match)) {              \__match = (void *)(e) + __i;                \\__ret = fn(__match , ## args);              \if (__ret != 0)                             \break;                              \}                                                   \if (__ret == 0) {                                   \if (__i != (e)->watchers_offset)            \__ret = -EINVAL;                    \}                                                   \__ret;                                              \
})

ebt_do_match

static inline int
ebt_do_match(struct ebt_entry_match *m, const struct sk_buff *skb,struct xt_action_param *par)
{par->match     = m->u.match;par->matchinfo = m->data;return m->u.match->match(skb, par) ? EBT_MATCH : EBT_NOMATCH; //调用具体过滤匹配match
}

ebt_ip_mt

举例看看ip的过滤匹配规则

static struct xt_match ebt_ip_mt_reg __read_mostly = {.name     = "ip",.revision = 0,.family        = NFPROTO_BRIDGE,.match        = ebt_ip_mt, //上面的m->u.match->match(skb, par).checkentry = ebt_ip_mt_check,.matchsize   = sizeof(struct ebt_ip_info),.me       = THIS_MODULE,
};
/* the same values are used for the invflags */
struct ebt_ip_info {__be32 saddr;__be32 daddr;__be32 smsk;__be32 dmsk;__u8  tos;__u8  protocol;__u8  bitmask; //存储了上层配置了哪些匹配选项,比如源ip地址,目的端口号等__u8  invflags;__u16 sport[2];__u16 dport[2];
};
static bool
ebt_ip_mt(const struct sk_buff *skb, struct xt_action_param *par)
{const struct ebt_ip_info *info = par->matchinfo; //匹配信息,上层配置信息保存const struct iphdr *ih;struct iphdr _iph;const struct tcpudphdr *pptr;struct tcpudphdr _ports;ih = skb_header_pointer(skb, 0, sizeof(_iph), &_iph);if (ih == NULL)return false;//根据bitmask 中配置的选项,进行匹配if (info->bitmask & EBT_IP_TOS &&FWINV(info->tos != ih->tos, EBT_IP_TOS))return false;if (info->bitmask & EBT_IP_SOURCE &&FWINV((ih->saddr & info->smsk) !=info->saddr, EBT_IP_SOURCE))return false;if ((info->bitmask & EBT_IP_DEST) &&FWINV((ih->daddr & info->dmsk) !=info->daddr, EBT_IP_DEST))return false;if (info->bitmask & EBT_IP_PROTO) {if (FWINV(info->protocol != ih->protocol, EBT_IP_PROTO))return false;if (!(info->bitmask & EBT_IP_DPORT) &&!(info->bitmask & EBT_IP_SPORT))return true;if (ntohs(ih->frag_off) & IP_OFFSET)return false;pptr = skb_header_pointer(skb, ih->ihl*4,sizeof(_ports), &_ports);if (pptr == NULL)return false;if (info->bitmask & EBT_IP_DPORT) {u32 dst = ntohs(pptr->dst);if (FWINV(dst < info->dport[0] ||dst > info->dport[1],EBT_IP_DPORT))return false;}if (info->bitmask & EBT_IP_SPORT) {u32 src = ntohs(pptr->src);if (FWINV(src < info->sport[0] ||src > info->sport[1],EBT_IP_SPORT))return false;}}return true;
}

linux kernel ebtables接口相关推荐

  1. Linux Kernel 6.0 CXL Core pci.c 详解

    文章目录 前言 相关链接 Ref 正文 前言 CXL 是一个比较新的技术,所以我研究的内核源码是选了当前比较新的内核版本 linux 6.0.打算将内核关于 CXL 的驱动进行解析一遍,一步一步慢慢来 ...

  2. Linux Kernel TCP/IP Stack — L7 Layer — Application Socket I/O 接口类型

    目录 文章目录 目录 基本概念 同步与异步 阻塞与非阻塞 I/O 操作的执行流程 Socket I/O 接口类型 阻塞 IO 缺点 非阻塞 IO 缺点 阻塞 IO 与非阻塞 IO 的区别 IO 多路复 ...

  3. linux内核seq_file接口

    seq相关头文件linux/seq_file.h,seq相关函数的实现在fs/seq_file.c.seq函数最早是在2001年就引入了,但以前内核中一直用得不多,而到了2.6内核后,许多/proc的 ...

  4. Kprobe在Linux kernel debug中的应用

    一直在做kernel开发方面的工作,也一直苦于kernel debug的困惑,到底如何进行kernel开发的debug的工作?今天经美国同事的推荐,我认为kprobe是一个非常好的debug工具.其本 ...

  5. Linux Kernel TCP/IP Stack — L1 Layer — 多队列网卡

    目录 文章目录 目录 多队列网卡 Intel 82575 的多队列硬件实现 Intel 82575 的多队列软件驱动实现 多队列网卡识别 多队列网卡 多队列网卡,是一种用来解决网络 I/O QoS 问 ...

  6. Linux Kernel TCP/IP Stack — L1 Layer — Network Interface

    目录 文章目录 目录 Linux 的网络设备接口 Linux 的虚拟网络设备接口 Linux 的网络设备接口 Network Interface(网络接口)通常是一个硬件设备,也可能是纯软件实现的,例 ...

  7. Linux Kernel TCP/IP Stack — 协议栈收包处理流程

    目录 文章目录 目录 L2 NIC Controller 收包处理流程 L3-4 收包处理流程 Socket Layer 收包处理流程 参考文档 L2 NIC Controller 收包处理流程 硬件 ...

  8. Linux Kernel TCP/IP Stack — L3 Layer — netfilter 框架

    目录 文章目录 目录 netfilter 框架 netfilter 的组成模块 netfilter 的 Hook 机制实现 netfilter 的工作原理 规则(Rules) 链(Chains) 表( ...

  9. Linux Kernel TCP/IP Stack — Overview

    目录 文章目录 目录 协议栈全景图 协议栈处理流程概览 协议栈收发包概览 协议栈的逻辑架构 协议栈的分层架构 协议栈的文件系统 协议栈的数据结构 协议栈全景图 协议栈处理流程概览 在 Linux Ke ...

最新文章

  1. JavaScript 基础,登录验证
  2. 鸿雁电器oa系统中决策支持模块效果
  3. Windows7 IIS 500-内部服务器错误的解决方法
  4. 图解Win7下安装Borland C++ 3.1失败记
  5. 海西数据获评优秀服务器租用服务商奖项
  6. python爬虫系列:12306票务信息爬虫
  7. Muduo 网络编程示例之四:Twisted Finger
  8. C语言试题九之s=(ln(1)+ln(2)+ln(3)+…+ln(m))^0.5,s作为函数值返回
  9. python写前端和js_Python之路【第十二篇】前端之jsdomejQuery
  10. [转载] JVM中对象的回收过程
  11. 安装Visual Studio 2005 SP1时遇到1718错误
  12. 使用Postman对Restful接口进行测试
  13. centos ssh服务开启
  14. freeswitch新增app接口
  15. 偏微分方程的引入及概述
  16. 计算机在bios设置用u盘启动不,bios设置u盘启动图解
  17. win8改win7 bios设置方法
  18. html制作钢铁侠心脏,心脏术后我变成了“钢铁侠”
  19. 深圳市专精特新企业申报条件及各区奖励政策重点介绍,补贴20-200万
  20. 2022年全球市场总线插头总体规模、主要生产商、主要地区、产品和应用细分研究报告

热门文章

  1. centos7安装达梦数据库dm8
  2. 糊糊作品--主题模板神偷
  3. 安装GNOME中文桌面环境
  4. POJ - 3278抓牛牛
  5. u盘插到电脑计算机里没有反应,我的U盘插到电脑上没有反应、是USB口的原因还是因为什么? 爱问知识人...
  6. rpo rto mysql_【重点】RTO和RPO详解
  7. 对于转台的转动惯量的一点思考
  8. android——实现NFC的读写
  9. ABP vNext 对接 Ant Design Vue 实现分页查询
  10. DolphinScheduler任务类型梳理