规则的添加
在规则初始化时,会注册添加函数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);