ip_route_output_slow注释

来源:互联网 发布:戈洛夫金力量数据 编辑:程序博客网 时间:2024/06/06 10:07

转载自basic coder

根据 http://basiccoder.com/intro-linux-kernel-hash-rt-2.html 和 http://blog.chinaunix.net/uid-24673811-id-1754229.html 改编


static int ip_route_output_slow(struct net *net, struct rtable **rp,const struct flowi *oldflp){u32 tos= RT_FL_TOS(oldflp); /*获取tos和当前的RTO_ONLINK(?)标志*/struct flowi fl = { .fl4_dst = oldflp->fl4_dst,    .fl4_src = oldflp->fl4_src,    .fl4_tos = tos & IPTOS_RT_MASK,    .fl4_scope = ((tos & RTO_ONLINK) ?  /*根据这个标志,得出路由的scope*/  RT_SCOPE_LINK : RT_SCOPE_UNIVERSE),    .mark = oldflp->mark,    .iif = net->loopback_dev->ifindex, /*设备号为回环设备的设备号?*/    .oif = oldflp->oif };struct fib_result res;unsigned int flags = 0;struct net_device *dev_out = NULL;int err;res.fi= NULL;#ifdef CONFIG_IP_MULTIPLE_TABLESres.r= NULL;#endif/*先是对源地址, 发包接口号和目的地址进行判断分类处理。*/  if (oldflp->fl4_src) {/* 若源地址为组播地址,受限广播地址(255.255.255.255)或0地址,均不合法,即刻返回 */err = -EINVAL;if (ipv4_is_multicast(oldflp->fl4_src) ||    ipv4_is_lbcast(oldflp->fl4_src) ||    ipv4_is_zeronet(oldflp->fl4_src))goto out;/*上面是对报文源地址的合理性检查,源地址是多播,广播或0地址时,返回错误*//* I removed check for oif == dev_out->oif here.   It was wrong for two reasons:   1. ip_dev_find(net, saddr) can return wrong iface, if saddr      is assigned to multiple interfaces.   2. Moreover, we are allowed to send packets with saddr      of another iface. --ANK */ /*当报文初始化的出接口为回环接口,源地址不为空且目的地址是多播或广播地址时,   找到源地址所对应的接口,并重新为出接口赋值, 然后创建cache路由项*/if (oldflp->oif == 0 &&    (ipv4_is_multicast(oldflp->fl4_dst) ||     ipv4_is_lbcast(oldflp->fl4_dst))) { /*发包接口为回环接口,目的地址是广播或多播时查找发包设备,ip_dev_find返回与所给定的源地址相等的第一个设备*/ /* 等价于inet_addr_type(saddr) == RTN_LOCAL, __ip_dev_find()函数实际是搜索RT_TABLE_LOCAL路由表中的路由表项,如果未找到对应设备则返回,因为Linux不允许环回接口发组播或受限广播 *//* It is equivalent to inet_addr_type(saddr) == RTN_LOCAL */dev_out = __ip_dev_find(net, oldflp->fl4_src, false);if (dev_out == NULL)goto out;/* Special hack: user can direct multicasts   and limited broadcast via necessary interface   without fiddling with IP_MULTICAST_IF or IP_PKTINFO.   This hack is not just for fun, it allows   vic,vat and friends to work.   They bind socket to loopback, set ttl to zero   and expect that it will work.   From the viewpoint of routing cache they are broken,   because we are not allowed to build multicast path   with loopback source addr (look, routing cache   cannot know, that ttl is zero, so that packet   will not leave this host and route is valid).   Luckily, this hack is good workaround. */ /* 给外面接口赋值后转去创建路由缓存 */ fl.oif = dev_out->ifindex;goto make_route;}/*本机出发的数据包的源地址如果有的话,必须要在local表中找到一条local路由FLOWI_FLAG_ANYSRC 标志可以取消本地地址必须存在于local路由表的限制*/if (!(oldflp->flags & FLOWI_FLAG_ANYSRC)) {/* It is equivalent to inet_addr_type(saddr) == RTN_LOCAL */if (!__ip_dev_find(net, oldflp->fl4_src, false))goto out;}}if (oldflp->oif) {/*发包设备不为空*/ /*检测出接口是否存在*/dev_out = dev_get_by_index_rcu(net, oldflp->oif);err = -ENODEV;if (dev_out == NULL)goto out;/* RACE: Check return value of inet_select_addr instead. *//* 如果外出接口示启用或外出接口对应的IPv4数据不存在,则返回网络不可达 */if (!(dev_out->flags & IFF_UP) || !__in_dev_get_rcu(dev_out)) {err = -ENETUNREACH;goto out;}/* 若是本地组播地址或受限广播地址则直接转去创建路由缓存 */if (ipv4_is_local_multicast(oldflp->fl4_dst) ||    ipv4_is_lbcast(oldflp->fl4_dst)) { /*当报文源地址为空时,找出出接口设备上IP地址scope小于RT_SCOPE_LINK的地址,并赋值,然后往cache中添加路由表项*/if (!fl.fl4_src)fl.fl4_src = inet_select_addr(dev_out, 0,      RT_SCOPE_LINK);goto make_route;}/* 若未指定源地址,则根据目地地址类型创建选择一个源地址 */if (!fl.fl4_src) {/*目的地址是单播地址或空,源地址为空,那就选一个小于特定scope的IP地址*/if (ipv4_is_multicast(oldflp->fl4_dst))fl.fl4_src = inet_select_addr(dev_out, 0,      fl.fl4_scope);else if (!oldflp->fl4_dst)fl.fl4_src = inet_select_addr(dev_out, 0,      RT_SCOPE_HOST);}}/* 如果目的地址不存在,则令目的地址等于源地址,若都不存在,则使用环回接口,路由类型为本地路由,转而创建路由缓存 */if (!fl.fl4_dst) {fl.fl4_dst = fl.fl4_src;if (!fl.fl4_dst)fl.fl4_dst = fl.fl4_src = htonl(INADDR_LOOPBACK);dev_out = net->loopback_dev;fl.oif = net->loopback_dev->ifindex;res.type = RTN_LOCAL;flags |= RTCF_LOCAL;goto make_route;}/*OK, 走到这里先总结一下不需要查询路由表即可直接创建路由缓存的情况:1. 指定了源地址,未指定外出接口,目的地址为组播地址或受限广播地址2. 指定了外出接口,并且目的地址为本地组播地址或受限广播地址3. 未指定目的地址。 若以上三种情况均未满足,则需要进行路由表查询。*/if (fib_lookup(net, &fl, &res)) {res.fi = NULL;if (oldflp->oif) {/* 程序走到这里说明查询路由表失败,未找到对应的路由表项,但却指定了外出接口,这时候即便没有路由也是可以发送数据包的。当然,如果未指定外出接口,则只能返回网络不可达了。 */if (fl.fl4_src == 0)fl.fl4_src = inet_select_addr(dev_out, 0,      RT_SCOPE_LINK);res.type = RTN_UNICAST;goto make_route;}err = -ENETUNREACH;goto out;}/* 若为本地路由,则使用环回接口 */if (res.type == RTN_LOCAL) {if (!fl.fl4_src) {if (res.fi->fib_prefsrc)fl.fl4_src = res.fi->fib_prefsrc;elsefl.fl4_src = fl.fl4_dst;}dev_out = net->loopback_dev;fl.oif = dev_out->ifindex;res.fi = NULL;flags |= RTCF_LOCAL;goto make_route;}#ifdef CONFIG_IP_ROUTE_MULTIPATHif (res.fi->fib_nhs > 1 && fl.oif == 0)fib_select_multipath(&fl, &res);else#endif/*  使用默认路由需要三个条件:1. 若前缀为0,也即掩码长度为0,默认路由匹配所有的目的地址。2. 路由类型为RTN_UNICAST,我们知道本地地址,组播地址和广播地址3. 未指定出口设备,上面我们提到即便是没有路由的情况下提供了出口设备,数据包也是可以发送的。这时候路由是默认路由,因此我们需要选择默认网关 */if (!res.prefixlen && res.type == RTN_UNICAST && !fl.oif)fib_select_default(net, &fl, &res);if (!fl.fl4_src)fl.fl4_src = FIB_RES_PREFSRC(res);dev_out = FIB_RES_DEV(res);fl.oif = dev_out->ifindex;make_route:/* 创建一条路由缓存 */err = ip_mkroute_output(rp, &res, &fl, oldflp, dev_out, flags);out:return err;}


原创粉丝点击