• 策略路由之添加


    规则的添加

    在规则初始化时,会注册添加函数fib_nl_newrule
    rtnl_register(PF_UNSPEC, RTM_NEWRULE, fib_nl_newrule, NULL);
    接下来,分析创建规则fib_nl_newrule函数
    功能:
    (1)根据应用层传递的协议类型,找到相应的fib_rules_ops变量;
    (2)解析应用层传入的数据;
    (3)针对源IP和目的IP,有效性检查
    (4)分配新的fib_rule缓存,并对优先级、接口index、接口名称、mark值、action等赋值;
    (5)调用协议对应的configure函数,对fib_rule的源IP、目的IP、掩码、tos进行配置;
    (6)将fib_rule添加到rule_lists中,并通过netlink通知其他进程。

    static int fib_nl_newrule(struct sk_buff *skb, struct nlmsghdr* nlh)
    {
        struct net *net = sock_net(skb->sk);
        struct fib_rule_hdr *frh = nlmsg_data(nlh);
        struct fib_rules_ops *ops = NULL;
        struct fib_rule *rule, *r, *last = NULL;
        struct nlattr *tb[FRA_MAX+1];
        int err = -EINVAL, unresolved = 0;
    
        if (nlh->nlmsg_len < nlmsg_msg_size(sizeof(*frh)))
            goto errout;
        //根据应用层传递的协议类型family af_inet ,找到相应的fib_rules_ops变量
            /*/ipv4对应的是 fib4_rules_ops
            根据协议簇 在链表rules_ops中查找符合要求的fib_rules_ops类型的变量;
            对于IPV4,变量为fib4_rules_ops。*/
        ops = lookup_rules_ops(net, frh->family);
        if (ops == NULL) {
            err = -EAFNOSUPPORT;
            goto errout;
        }
        //解析应用层传入的数据nlh,放入tb中。
        err = nlmsg_parse(nlh, sizeof(*frh), tb, FRA_MAX, ops->policy);
        if (err < 0)
            goto errout;
    
        err = validate_rulemsg(frh, tb, ops);//有效性检查,针对源IP和目的IP
        if (err < 0)
            goto errout;
    
        rule = kzalloc(ops->rule_size, GFP_KERNEL);//分配新的fib_rule缓存
        if (rule == NULL) {
            err = -ENOMEM;
            goto errout;
        }
        rule->fr_net = net;//增加rule->net的引用计数
    
        rule->pref = tb[FRA_PRIORITY] ? nla_get_u32(tb[FRA_PRIORITY])
                                      : fib_default_rule_pref(ops);//设置优先级
    
        if (tb[FRA_IIFNAME]) {
            struct net_device *dev;
    
            rule->iifindex = -1;//入接口index
            nla_strlcpy(rule->iifname, tb[FRA_IIFNAME], IFNAMSIZ);
            dev = __dev_get_by_name(net, rule->iifname);//通过接口名找dev
            if (dev)
                rule->iifindex = dev->ifindex;//将dev的index赋值给rule接口index
        }
    
        if (tb[FRA_OIFNAME]) {
            struct net_device *dev;
    
            rule->oifindex = -1;//和入接口一样 设置
            nla_strlcpy(rule->oifname, tb[FRA_OIFNAME], IFNAMSIZ);
            dev = __dev_get_by_name(net, rule->oifname);
            if (dev)
                rule->oifindex = dev->ifindex;
        }
    
        if (tb[FRA_FWMARK]) {
            rule->mark = nla_get_u32(tb[FRA_FWMARK]);//mark值
            if (rule->mark)//mark值
                /* compatibility: if the mark value is non-zero all bits
                 * are compared unless a mask is explicitly specified.
                 */
                rule->mark_mask = 0xFFFFFFFF;//默认 mark掩码值
        }
    
        if (tb[FRA_FWMASK])
            rule->mark_mask = nla_get_u32(tb[FRA_FWMASK]);
    
        if (tb[FRA_TUN_ID])
            rule->tun_id = nla_get_be64(tb[FRA_TUN_ID]);
        
        //设置规则action,table id,实现规则与路由表的关联
        rule->action = frh->action;
        rule->flags = frh->flags;
        rule->table = frh_get_table(frh, tb);
        if (tb[FRA_SUPPRESS_PREFIXLEN])
            rule->suppress_prefixlen = nla_get_u32(tb[FRA_SUPPRESS_PREFIXLEN]);
        else
            rule->suppress_prefixlen = -1;
    
        if (tb[FRA_SUPPRESS_IFGROUP])
            rule->suppress_ifgroup = nla_get_u32(tb[FRA_SUPPRESS_IFGROUP]);
        else
            rule->suppress_ifgroup = -1;
    
        err = -EINVAL;
        if (tb[FRA_GOTO]) {
            if (rule->action != FR_ACT_GOTO)
                goto errout_free;
    
            rule->target = nla_get_u32(tb[FRA_GOTO]);
            /* Backward jumps are prohibited to avoid endless loops */
            if (rule->target <= rule->pref)
                goto errout_free;
    
            list_for_each_entry(r, &ops->rules_list, list) {
                if (r->pref == rule->target) {
                    RCU_INIT_POINTER(rule->ctarget, r);
                    break;
                }
            }
    
            if (rcu_dereference_protected(rule->ctarget, 1) == NULL)
                unresolved = 1;
        } else if (rule->action == FR_ACT_GOTO)
            goto errout_free;
     //调用协议对应的configure函数,这里就是协议相关的添加
        err = ops->configure(rule, skb, frh, tb); 
     //ipv4的配置函数,根据应用层传参,设置fib4_rule变量参数:源IP、目的IP、掩码、路由表ID、tos
        if (err < 0)
            goto errout_free;
        //找到第一个pref比新创建的fib_rule的pref值大的 fib_rule
            //然后将新创建的fib_rule添到该规则之前
            //若没有找到,则添加到已有规则链表之后
        list_for_each_entry(r, &ops->rules_list, list) {
            if (r->pref > rule->pref)
                break;
            last = r;
        }
    
        fib_rule_get(rule);//增加fib_rule的引用计数
    
        if (last) //根据优先级,将fib_rule添到rules_list链表中
            list_add_rcu(&rule->list, &last->list);
        else
            list_add_rcu(&rule->list, &ops->rules_list);
    
        if (ops->unresolved_rules) {
            /*
             * There are unresolved goto rules in the list, check if
             * any of them are pointing to this new rule.
             */
            list_for_each_entry(r, &ops->rules_list, list) {
                if (r->action == FR_ACT_GOTO &&
                    r->target == rule->pref &&
                    rtnl_dereference(r->ctarget) == NULL) {
                    rcu_assign_pointer(r->ctarget, rule);
                    if (--ops->unresolved_rules == 0)
                        break;
                }
            }
        }
    
        if (rule->action == FR_ACT_GOTO)
            ops->nr_goto_rules++;
    
        if (unresolved)
            ops->unresolved_rules++;
    
        if (rule->tun_id)
            ip_tunnel_need_metadata();
        //通过netlink通知其他进程
        notify_rule_change(RTM_NEWRULE, rule, ops, nlh, NETLINK_CB(skb).portid);
        flush_route_cache(ops);//刷新路由缓存
        rules_ops_put(ops);
        return 0;
    
    errout_free:
        kfree(rule);
    errout:
        rules_ops_put(ops);
        return err;
    }

    策略路由查找

    策略规则的查找函数fib_rules_lookup
    功能:

    (1)遍历rules_list链表;

    (2)调用fib_rule_match进行规则匹配

    (3)调用函数指针action,进行路由表项的查找;
    (4)将arg->rule指向该规则的首地址。

    int fib_rules_lookup(struct fib_rules_ops *ops, struct flowi *fl,
                 int flags, struct fib_lookup_arg *arg)
    {
        struct fib_rule *rule;
        int err;
    
        rcu_read_lock();
    
        list_for_each_entry_rcu(rule, &ops->rules_list, list) {//遍历ops->rules_list的所有fib_rule节点;比如 ipv4-rules 
    jumped:
    
            pr_info("family:%d tableid:%d pref:0X%x  src:%pI4--->dst:%pI4\n", ops->family, rule->table, rule->pref, 
                    &(fl->u.ip4.saddr), &(fl->u.ip4.daddr));
            pr_info("rule  iiindex:%d  oifindex:%d mark:0x%x mark_mask:0x%x flags:%d targer:%d action:%d\n",
                rule->iifindex, rule->oifindex, rule->mark, rule->mark_mask, rule->flags, rule->target,
                rule->action);
            /*对于传入的fib_rule变量与传入的路由查找键值flowi变量比较
                //判断输入接口index是否相等;mark是否相等
                /源IP、目的IP是否相等
                //如果tos 非0; tos是否相等
                //协议规则的匹配,ipv4调用fib4_rule_match
            */
            if (!fib_rule_match(rule, ops, fl, flags)) //规则匹配
                continue;
            pr_info("family:%d tableid:%d pref:0X%x action:%d\n", ops->family, rule->table, rule->pref, rule->action);
            if (rule->action == FR_ACT_GOTO) {
                struct fib_rule *target;
    
                target = rcu_dereference(rule->ctarget);
                if (target == NULL) {
                    continue;
                } else {
                    rule = target;
                    goto jumped;
                }
            } else if (rule->action == FR_ACT_NOP)
                continue;
            else
                err = ops->action(rule, fl, flags, arg);//fib4_rule_action 查找对应的路由表项
                //获取到相应的路由表 以及  查找符合要求的路由项;其逻辑/是/根据id值,获取相应的路由表,然后在tb 中查找fib 结果
            pr_info("action:%d err:%d \n", rule->action, err);
            if (!err && ops->suppress && ops->suppress(rule, arg))
                continue;
    
            if (err != -EAGAIN) {
                struct fib_result * res = arg->result;
                struct fib_info *pfib = res->fi;
            
                pr_info("prefixlen:%d nh_sel:%d type:%d scope:%d tb_id:%d prefsrc:%pI4 priority:%d oifname:%s nexthop:%pI4 \n",
                       res->prefixlen, res->nh_sel, res->type, res->scope, res->table->tb_id, 
                       &pfib->fib_prefsrc, pfib->fib_priority, pfib->fib_nh[0].nh_dev->name, &pfib->fib_nh[0].nh_gw);
    
                if ((arg->flags & FIB_LOOKUP_NOREF) ||
                    likely(atomic_inc_not_zero(&rule->refcnt))) {
                    arg->rule = rule;
                    goto out;
                }
                break;
            }
        }
    
        err = -ESRCH;
    out:
        rcu_read_unlock();
    
        return err;
    }
    EXPORT_SYMBOL_GPL(fib_rules_lookup);
  • 相关阅读:
    JavaScript DOM编程艺术 读书笔记(简略)
    关于暂停或终止更新的相关读书笔记
    Core Java Volume II—Using annotations
    Data Structure and Algorithms Analysis in C Note (II)
    Hibernate实战——理解对象/关系持久化 笔记
    Data Structure and Algorithms Analysis in C Note (I)
    Java 8实战 第三章
    GitHub入门与实践 学习笔记(二)
    Computer Networking A Top-Down Approach 笔记(一)
    进程基础
  • 原文地址:https://www.cnblogs.com/codestack/p/15964315.html
Copyright © 2020-2023  润新知