Chinaunix首页 | 论坛 | 博客
  • 博客访问: 85191
  • 博文数量: 9
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 100
  • 用 户 组: 普通用户
  • 注册时间: 2014-12-23 13:15
文章分类
文章存档

2017年(2)

2016年(1)

2015年(4)

2014年(2)

我的朋友

分类: LINUX

2017-01-24 13:48:40


   在项目开发中对255.255.255.255的路由转发一直很疑惑,不能从常规的报文转发处理逻辑理解清楚。

问题背景:
    Linux设备的应用程序发送目的地址为255.255.255.255的UDP广播报文,同时也有一个应用程序接收UDP(与前一个发送的UDP端口号相同)
   在设备上添加路由255.255.255.255的静态路由:
   255.255.255.255 via 198.1.19.1 dev enp3s0 

   从理论上,发送255.255.255.255的报文,查找路由表,只会从enp3s0接口发送出去。但是实际情况是,本地UDP进程也能收到报文,同时z在enp3s0接口tcpdump也能抓到报文。也就是说对255.255.255.255的报文,不只是发送到路由出接口。

   查找Linux内核源码,处理255.255.255.255报文处理流程。
    本地发送报文,开始时先进行慢转发,查找路由表,查找到路由后,添加到路由缓存中
   ip_route_output_slow()
    ------fib_lookup()
   ------__mkroute_output()

static struct rtable *__mkroute_output(const struct fib_result *res,
      const struct flowi4 *fl4,
      __be32 orig_daddr, __be32 orig_saddr,
      int orig_oif, struct net_device *dev_out,
      unsigned int flags)
{
    struct fib_info *fi = res->fi;
    u32 tos = RT_FL_TOS(fl4);
    struct in_device *in_dev;
    u16 type = res->type;
    truct rtable *rth;


    if (ipv4_is_loopback(fl4->saddr) && !(dev_out->flags & IFF_LOOPBACK))
        return ERR_PTR(-EINVAL);


    if (ipv4_is_lbcast(fl4->daddr))
        type = RTN_BROADCAST;     //置为广播
    else if (ipv4_is_multicast(fl4->daddr))
        type = RTN_MULTICAST;
    else if (ipv4_is_zeronet(fl4->daddr))
        return ERR_PTR(-EINVAL);


    if (dev_out->flags & IFF_LOOPBACK)
        flags |= RTCF_LOCAL;


    in_dev = __in_dev_get_rcu(dev_out);
    if (!in_dev)
        return ERR_PTR(-EINVAL);


    if (type == RTN_BROADCAST) {
        flags |= RTCF_BROADCAST | RTCF_LOCAL;
        fi = NULL;
    } else if (type == RTN_MULTICAST) {
        flags |= RTCF_MULTICAST | RTCF_LOCAL;
        if (!ip_check_mc_rcu(in_dev, fl4->daddr, fl4->saddr,
            fl4->flowi4_proto))
    flags &= ~RTCF_LOCAL;
/* If multicast route do not exist use
* default one, but do not gateway in this case.
* Yes, it is hack.
*/
if (fi && res->prefixlen < 4)
fi = NULL;
}

rth = rt_dst_alloc(dev_out,
  IN_DEV_CONF_GET(in_dev, NOPOLICY),
  IN_DEV_CONF_GET(in_dev, NOXFRM));
if (!rth)
return ERR_PTR(-ENOBUFS);

rth->dst.output = ip_output;

rth->rt_key_dst = orig_daddr;
rth->rt_key_src = orig_saddr;
rth->rt_genid = rt_genid(dev_net(dev_out));
rth->rt_flags = flags;
rth->rt_type = type;
rth->rt_key_tos = tos;
rth->rt_dst = fl4->daddr;
rth->rt_src = fl4->saddr;
rth->rt_route_iif = 0;
rth->rt_iif = orig_oif ? : dev_out->ifindex;
rth->rt_oif = orig_oif;
rth->rt_mark    = fl4->flowi4_mark;
rth->rt_gateway = fl4->daddr;
rth->rt_spec_dst= fl4->saddr;
rth->rt_peer_genid = 0;
rth->peer = NULL;
rth->fi = NULL;

RT_CACHE_STAT_INC(out_slow_tot);

if (flags & RTCF_LOCAL) {
rth->dst.input = ip_local_deliver;
rth->rt_spec_dst = fl4->daddr;
}
if (flags & (RTCF_BROADCAST | RTCF_MULTICAST)) {
rth->rt_spec_dst = fl4->saddr;
if (flags & RTCF_LOCAL &&
   !(dev_out->flags & IFF_LOOPBACK)) {
rth->dst.output = ip_mc_output;   //输出函数置为ip_mc_output
RT_CACHE_STAT_INC(out_slow_mc);
}
#ifdef CONFIG_IP_MROUTE
if (type == RTN_MULTICAST) {
if (IN_DEV_MFORWARD(in_dev) &&
   !ipv4_is_local_multicast(fl4->daddr)) {
rth->dst.input = ip_mr_input;
rth->dst.output = ip_mc_output;
}
}
#endif
}

rt_set_nexthop(rth, fl4, res, fi, type, 0);

return rth;
}
rth->rt_spec_dst = fl4->saddr;
if (flags & RTCF_LOCAL &&
   !(dev_out->flags & IFF_LOOPBACK)) {
rth->dst.output = ip_mc_output;
RT_CACHE_STAT_INC(out_slow_mc);
}
#ifdef CONFIG_IP_MROUTE
if (type == RTN_MULTICAST) {
if (IN_DEV_MFORWARD(in_dev) &&
   !ipv4_is_local_multicast(fl4->daddr)) {
rth->dst.input = ip_mr_input;
rth->dst.output = ip_mc_output;
}
}
#endif
}

再看函数ip_mc_output:
int ip_mc_output(struct sk_buff *skb)
{
struct sock *sk = skb->sk;
struct rtable *rt = skb_rtable(skb);
struct net_device *dev = rt->dst.dev;

/*
* If the indicated interface is up and running, send the packet.
*/
IP_UPD_PO_STATS(dev_net(dev), IPSTATS_MIB_OUT, skb->len);

skb->dev = dev;
skb->protocol = htons(ETH_P_IP);

/*
* Multicasts are looped back for other local users
*/

if (rt->rt_flags&RTCF_MULTICAST) {
if (sk_mc_loop(sk)
#ifdef CONFIG_IP_MROUTE
/* Small optimization: do not loopback not local frames,
  which returned after forwarding; they will be  dropped
  by ip_mr_input in any case.
  Note, that local frames are looped back to be delivered
  to local recipients.

  This check is duplicated in ip_mr_input at the moment.
*/
   &&
   ((rt->rt_flags & RTCF_LOCAL) ||
    !(IPCB(skb)->flags & IPSKB_FORWARDED))
#endif
  ) {
struct sk_buff *newskb = skb_clone(skb, GFP_ATOMIC);
if (newskb)
NF_HOOK(NFPROTO_IPV4, NF_INET_POST_ROUTING,
newskb, NULL, newskb->dev,
ip_dev_loopback_xmit);
}

/* Multicasts with ttl 0 must not go beyond the host */

if (ip_hdr(skb)->ttl == 0) {
kfree_skb(skb);
return 0;
}
}

/*如果是广播报文,拷贝一份报文,重新注入到协议栈,所有本机进程能收到报文*/
if (rt->rt_flags&RTCF_BROADCAST) {
struct sk_buff *newskb = skb_clone(skb, GFP_ATOMIC);
if (newskb)
NF_HOOK(NFPROTO_IPV4, NF_INET_POST_ROUTING, newskb,
NULL, newskb->dev, ip_dev_loopback_xmit);
}

/*在按照路由缓存结果,发送到路由的出接口*/
return NF_HOOK_COND(NFPROTO_IPV4, NF_INET_POST_ROUTING, skb, NULL,
   skb->dev, ip_finish_output,
   !(IPCB(skb)->flags & IPSKB_REROUTED));
}

也就是说,发送255.255.255.255的广播报文时,内核回拷贝一份报文,环回给自己。


阅读(3736) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~