博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Netfilter/iptables的一些新进展
阅读量:7081 次
发布时间:2019-06-28

本文共 4811 字,大约阅读时间需要 16 分钟。

关注了一下Netfilter的最新进展,新东西还真不少哇!但是最让人心动的有两个。

一.新的bpf模块

基于Linux kernel 3.9版本的patch是xt_bpf的支持,对应的iptables模块是libxt_bpf,这个在iptables-1.4.19版本中已经支持,顾名思义,bpf其实就是伯克利包过滤的缩写,对于它的描述,参见《 》。从名称上看,BPF理应就是iptables包过滤的首选技术,但是不知道什么原因,xt_tables一直维护着自己的数据结构保存rule的match和target,现在有了BPF的支持,我想内核协议栈在处理iptables规则时的效率会高很多,以往的一系列的多个match,现在封装进一个match,该match可以通过bytecode参数来指示,它是已经编译好的字节码,内核直接去执行,速度非常快。以往的match匹配基本就是遍历,现在基于bpf的不再依赖遍历了,而是去“执行”那段bytecode!

        tcpdump工具一直以来都是基于BPF的,虽然它的match在语法上和iptables的极其类似,其匹配效率却比iptales高很多,类似
-i eth2 tcp port 1234 and host 1.2.3.4
这一串匹配在iptables中需要建立4个entry,然而使用BPF的话,就可以编译成一段以下顺序执行的代码:
1.加载dev字段
2.判断dev字段,如果不是eth2则跳到x
3.加载协议字段
4.判断协议字段,如果不是tcp则跳到x
...
x.返回
这段代码类似汇编代码,被内核解释执行。不过,我在kernel 2.6.32上开始没有编译成功,因为这个版本太老了,很多接口和新的内核都不兼容,改了好久才勉强能运行,但是不能插入复杂的bytecode,否则就panic!
       不管怎样,采用这个BPF的架构,内核空间的代码执行效率会提高很多,并且代码量也会减少很多,像ipt_do_table这个巨无霸函数也能瘦身了。

二.最新的nftables项目

说到iptables内核代码的瘦身,Netfilter网站上开辟了另外一条路,那就是 ,它旨在完全替换掉既有的iptables/ebtables/arptables以及对应的v6版本。

    nftables最主要的革新在于两点,其一就是命令语法的完全改变,第二就是内核代码的优化。它采取了类似BPF的过滤方式,其matches的匹配过程就是一个状态机转换的过程,最终的终止节点就是target。在代码层面,它彻底改变了iptables对match存储的混乱场面,以下是匹配的核心代码:

//更加合理的数据结构,比iptables的平坦化的数据结构布局好多了struct nft_expr {    const struct nft_expr_ops    *ops;    unsigned char            data[];};struct nft_rule {    struct list_head        list;    struct list_head        dirty_list;    struct rcu_head            rcu_head;    u64                handle:46,                    genmask:2,                    dlen:16;    unsigned char            data[]        __attribute__((aligned(__alignof__(struct nft_expr))));};//net/netfilter/nf_tables_core.cunsigned intnft_do_chain_pktinfo(struct nft_pktinfo *pkt, const struct nf_hook_ops *ops){    const struct nft_chain *chain = ops->priv;    const struct nft_rule *rule;    const struct nft_expr *expr, *last;    struct nft_data data[NFT_REG_MAX + 1];    unsigned int stackptr = 0;    struct nft_jumpstack jumpstack[NFT_JUMP_STACK_SIZE];    int rulenum = 0;    /*     * Cache cursor to avoid problems in case that the cursor is updated     * while traversing the ruleset.     */    unsigned int gencursor = chain->net->nft.gencursor;do_chain:    rule = list_entry(&chain->rules, struct nft_rule, list);next_rule:    data[NFT_REG_VERDICT].verdict = NFT_CONTINUE;    list_for_each_entry_continue_rcu(rule, &chain->rules, list) {        /* This rule is not active, skip. */        if (unlikely(rule->genmask & (1 << gencursor)))            continue;        rulenum++;                       nft_rule_for_each_expr(expr, last, rule) {            if (expr->ops == &nft_cmp_fast_ops)                nft_cmp_fast_eval(expr, data);            else if (expr->ops != &nft_payload_fast_ops ||                 !nft_payload_fast_eval(expr, data, pkt))                expr->ops->eval(expr, data, pkt);            if (data[NFT_REG_VERDICT].verdict != NFT_CONTINUE)                break;        }        switch (data[NFT_REG_VERDICT].verdict) {        case NFT_BREAK:            data[NFT_REG_VERDICT].verdict = NFT_CONTINUE;            /* fall through */        case NFT_CONTINUE:            continue;        }        break;    }    switch (data[NFT_REG_VERDICT].verdict) {       //结果判定        case NF_ACCEPT:    case NF_DROP:    case NF_QUEUE:        nft_chain_stats(chain, pkt, jumpstack, stackptr);        if (unlikely(pkt->skb->nf_trace))            nft_trace_packet(pkt, chain, rulenum, NFT_TRACE_RULE);        return data[NFT_REG_VERDICT].verdict;    case NFT_JUMP:        //stack结构更好地组织了rule                if (unlikely(pkt->skb->nf_trace))            nft_trace_packet(pkt, chain, rulenum, NFT_TRACE_RULE);        BUG_ON(stackptr >= NFT_JUMP_STACK_SIZE);        jumpstack[stackptr].chain = chain;        jumpstack[stackptr].rule  = rule;        jumpstack[stackptr].rulenum = rulenum;        stackptr++;        /* fall through */    case NFT_GOTO:        chain = data[NFT_REG_VERDICT].chain;        goto do_chain;    case NFT_RETURN:        if (unlikely(pkt->skb->nf_trace))            nft_trace_packet(pkt, chain, rulenum, NFT_TRACE_RETURN);        /* fall through */    case NFT_CONTINUE:        break;    default:        WARN_ON(1);    }    if (stackptr > 0) {        if (unlikely(pkt->skb->nf_trace))            nft_trace_packet(pkt, chain, ++rulenum, NFT_TRACE_RETURN);        stackptr--;        chain = jumpstack[stackptr].chain;        rule  = jumpstack[stackptr].rule;        rulenum = jumpstack[stackptr].rulenum;        goto next_rule;    }    nft_chain_stats(chain, pkt, jumpstack, stackptr);    if (unlikely(pkt->skb->nf_trace))        nft_trace_packet(pkt, chain, ++rulenum, NFT_TRACE_POLICY);    return nft_base_chain(chain)->policy;}

新的nftables瘦身的原因在于大量采用了回调函数,使判定逻辑独立出来,核心的do_tables变成了一个单纯的状态机!这个抽取动作带来了的效果就是rule更加灵活了,类似BPF的思想,数据包可以根据每一步的结果在不同的rule或者不同的match之间任意跳转了。相比iptables在匹配过程中的大量判断,结果硬编码,nftables确实有一个质的飞跃,期待nftables早日出炉!

 

 

转载地址:http://ewlml.baihongyu.com/

你可能感兴趣的文章
Spring、Spring Boot和TestNG测试指南 - 测试Spring MVC
查看>>
Memory Management and Circular References in Python
查看>>
用node+express+mongoDB实现用户登录注册模板
查看>>
微信小程序server-2-实现会话层
查看>>
vim下处理文档中的\r\n\t字符
查看>>
php常见术语
查看>>
看例子,学 Python(三)
查看>>
git心得
查看>>
头部组件header.vue
查看>>
golang - 收藏集 - 掘金
查看>>
Promise 的几种通用模式
查看>>
Kubelet源码分析(四) diskSpaceManager
查看>>
用Org-Mode和Jekyll写博客
查看>>
AlphaZero进化论:从零开始,制霸所有棋类游戏
查看>>
创新技术重塑未来物联网
查看>>
庖丁解牛!深入剖析React Native下一代架构重构
查看>>
架构师的狂欢—ArchSummit深圳2016等您来约
查看>>
访谈Stuart Davidson:Skyscanner的持续交付推广
查看>>
Oracle把Java EE的未来押在Rest API上了?
查看>>
Vue性能优化:如何实现延迟加载和代码拆分?
查看>>