connect 路由 ip_route_connect
ip_route_connect_init -> flowi4_init_output
这个函数会去初始化fl4的数据,例如fl4->daddr,fl4->saddr,fl4->fl4_dport,fl4->fl4_sport,但是并非都是已经设置好值的,因为想要发送第一个SYN报文,就需要完整的来源/目的地址,来源/目的端口,然而在客户端的socket connect中,只有目的地址/端口可以被用户提供出来,来源端口可以从未用端口中动态分配一个,然而一个主机上可能有多个IP,那么来源地址如果错误的话,就很有可能造成数据不可达,因此这时候需要有路由参与在内。
而关于这个来源地址在接口层次上还分为bind和no-bind类型,如果是bind类型的话来源地址就是bind地址,但是no-bind类型的话则需要根据路由的结果确定来源地址。
一个数据包传输到本地后,需要先在IP层查找路由,然后在传输层查找socket,因此为了高效利用,linux内核提供了一个特性ip_early_demux,其能力就是在socket中增加一个dst字段作为缓存路由,skb在查找路由前优先查找socket,找到的话就把缓存的dst设置到skb,那么再去查找路由的时候发现已经有dst了,就省去查找路由的过程
static inline struct rtable *ip_route_connect(struct flowi4 *fl4, __be32 dst, __be32 src, u32 tos, int oif, u8 protocol, __be16 sport, __be16 dport, struct sock *sk) { struct net *net = sock_net(sk); struct rtable *rt; ip_route_connect_init(fl4, dst, src, tos, oif, protocol, sport, dport, sk); if (!dst || !src) { rt = __ip_route_output_key(net, fl4); if (IS_ERR(rt)) return rt; ip_rt_put(rt); flowi4_update_output(fl4, oif, tos, fl4->daddr, fl4->saddr); } security_sk_classify_flow(sk, flowi4_to_flowi(fl4)); return ip_route_output_flow(net, fl4, sk); }
初始化完fl4后会有一个判断,因为src是没有设置的,所以会自动进入到__ip_route_output_key的流程,接着在ip_route_output_key_hash中填充一些fl4的数据然后进入到ip_route_output_key_hash_rcu中
函数开始便是三个路由条件判断:
if (fl4->saddr) { //来源地址,为空 if (fl4->flowi4_oif) { //出口设备,为0 if (!fl4->daddr) { //目标地址,为用户定义地址
三个条件都没有符合,直接进入到err = fib_lookup(net, fl4, res, 0);函数,这个是路由转发表检索函数,fl4是查找条件,而res是路由查找结果。
因为此时的来源地址为空,那么策略路由的from关键字将无法匹配到所有没有bind地址的程序发出的数据包,实际上来说这就导致不会进入到任何一张自定义策略路由表中。
简单点说,策略路由就是根据来源地址,目的地址,入接口,出接口等元素决定数据包在路由前是否进入到该张策略路由表。
err = fib_rules_lookup(net->ipv4.rules_ops, flowi4_to_flowi(flp), 0, &arg);net->ipv4.rules_ops中的rules_list链表保存了当前net namespace所有的路由策略。通过list_for_each_entry_rcu遍历链表,然后通过fib_rule_match匹配策略
而关于函数match的方法
通用规则匹配
1)如指定入接口,数据包的入接口必须和策略rule中指定的入接口相同; //iif eth1
2)如指定出接口,二者的出接口必须相同; //oif eth0
3)如指定流标记,二者的流标记必须相同; //fwmark 0x3
4)如指定隧道ID,二者的隧道ID必须相同; //tunid
另外,针对IPv4协议:
5)策略rule中指定的源IP地址与数据包的源IP地址,与掩码进行位与操作,结果必须相同;//from
5)策略rule中指定的目的IP地址与数据包的目的IP地址,与掩码进行位与操作,结果必须相同; //to
7)如指定TOS值,二者的TOS值必须相同; //服务类型,即指定服务的流量 //tos
首先遍历到的0: from all lookup local在进入到fib4_rule_match后会因为什么都没有设置而直接返回1,也就是直接匹配到。此刻本条规则的大致数据如下:
r->action = FR_ACT_TO_TBL; r->pref = 0; r->table = RT_TABLE_LOCAL; r->flags = 0;
匹配到规则后返回至fib_rules_lookup,判断下action接后执行ops->action的操作,此刻应该是fib4_rule_action,在没有使用l3mdev的情况下,使用rule->table作为table id,然后调用fib_get_table获取该表,最后通过fib_table_lookup进行路由项查找。err = 0,又因为res->type = RTN_LOCAL,且fl4->saddr = 0,所以fl4->saddr = 127.0.0.1且fl4->flowi4_oif = loopback_dev,__mkroute_output。 直接看到rt_set_nexthop(rth, fl4->daddr, res, fnhe, fi, type, 0, do_cache);,然后重新填充fl4的数据
flowi4_update_output
/* Reset some input parameters after previous lookup */ static inline void flowi4_update_output(struct flowi4 *fl4, int oif, __u8 tos, __be32 daddr, __be32 saddr) { fl4->flowi4_oif = oif; fl4->flowi4_tos = tos; fl4->daddr = daddr; fl4->saddr = saddr; }

浙公网安备 33010602011771号