dst_output发包

不管是收到报文转发还是本机发送报文,最后都会调用dst_output

复制代码
/* Output packet to network from transport.  */
static inline int dst_output(struct net *net, struct sock *sk, struct sk_buff *skb)
{
   /*
     * 如果是单播数据包,设置的是ip_output(),
     * 如果是组播数据包,设置的是ip_mc_output().dev_queue_xmit
     */
    return skb_dst(skb)->output(net, sk, skb);
}
复制代码

 单播:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
/*
 * 对于单播数据包,目的路由缓存项中的输出接口是ip_output().
 */
int ip_output(struct net *net, struct sock *sk, struct sk_buff *skb)
{
    struct net_device *dev = skb_dst(skb)->dev;
 
    IP_UPD_PO_STATS(net, IPSTATS_MIB_OUT, skb->len);
    /*
     * 设置数据包的输出网络设备和数据包网络
     * 层协议类型。
     */
    skb->dev = dev;
    skb->protocol = htons(ETH_P_IP);
/*
     * 经netfilter处理后,调用ip_finish_output()继续IP数据包的输出
     */
    return NF_HOOK_COND(NFPROTO_IPV4, NF_INET_POST_ROUTING,
                net, sk, skb, NULL, dev,
                ip_finish_output,
                !(IPCB(skb)->flags & IPSKB_REROUTED));
}

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
static int ip_finish_output(struct net *net, struct sock *sk, struct sk_buff *skb)
{
    unsigned int mtu;
 
#if defined(CONFIG_NETFILTER) && defined(CONFIG_XFRM)
    /* Policy lookup after SNAT yielded a new policy */
    if (skb_dst(skb)->xfrm) {
        IPCB(skb)->flags |= IPSKB_REROUTED;
        return dst_output(net, sk, skb);
    }
#endif
/*
//如果不支持TSO或者GSO,tcp发送的时候是按照mss来组织skb的,
所以skb->len会等于mtu  所以TCP叫分段,和IP分片不一样,只有UDP才有IP分片
//SKB不是gso类型,并且skb->len大于mtu则需要分片 
对方接受后的分片重组在netfilter中的ipv4_conntrack_defrag
*/
    mtu = ip_skb_dst_mtu(sk, skb);
    if (skb_is_gso(skb))
        return ip_finish_output_gso(net, sk, skb, mtu);
 /* 如果数据包长度大于MTU,则调用ip_fragment()
     * 对IP数据包进行分片。
     */
    if (skb->len > mtu || (IPCB(skb)->flags & IPSKB_FRAG_PMTU))
        return ip_fragment(net, sk, skb, mtu, ip_finish_output2);
 
    return ip_finish_output2(net, sk, skb);
}

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
/* ip send the packet  by ip_finish_output2*/
static int ip_finish_output2(struct net *net, struct sock *sk, struct sk_buff *skb)
{
    struct dst_entry *dst = skb_dst(skb);
    struct rtable *rt = (struct rtable *)dst;
    struct net_device *dev = dst->dev;
    unsigned int hh_len = LL_RESERVED_SPACE(dev);
    struct neighbour *neigh;
    u32 nexthop;
 
    if (rt->rt_type == RTN_MULTICAST) {
        IP_UPD_PO_STATS(net, IPSTATS_MIB_OUTMCAST, skb->len);
    } else if (rt->rt_type == RTN_BROADCAST)
        IP_UPD_PO_STATS(net, IPSTATS_MIB_OUTBCAST, skb->len);
 
    /* Be paranoid, rather than too clever. */
     /* skb头部空间不能存储链路头 */
    if (unlikely(skb_headroom(skb) < hh_len && dev->header_ops)) {
        struct sk_buff *skb2;
         /* 重新分配skb */
        skb2 = skb_realloc_headroom(skb, LL_RESERVED_SPACE(dev));
        if (!skb2) {
            kfree_skb(skb);
            return -ENOMEM;
        }
        if (skb->sk)/* 关联控制块 */
            skb_set_owner_w(skb2, skb->sk);
        consume_skb(skb); /* 释放skb */
        skb = skb2; /* 指向新的skb */
    }
 
    rcu_read_lock_bh();
    /* 获取下一跳 */
    nexthop = (__force u32) rt_nexthop(rt, ip_hdr(skb)->daddr);// get the dst ip address (u32)
    neigh = __ipv4_neigh_lookup_noref(dev, nexthop);
    //根据目的IP查找邻居项是否存在 
    //如果没有则创建邻居项,然后通过dst_neigh_output  发包
    if (unlikely(!neigh))
        neigh = __neigh_create(&arp_tbl, &nexthop, dev, false);
    if (!IS_ERR(neigh)) {/* 成功 */
        int res = dst_neigh_output(dst, neigh, skb); /* 通过邻居子系统输出 */
 
        rcu_read_unlock_bh();
        return res;
    }
    rcu_read_unlock_bh();
 
    net_dbg_ratelimited("%s: No header cache and no neighbour!\n",
                __func__);
    kfree_skb(skb);
    return -EINVAL;
}

 

最后通过令邻居子系统,调用dev_queue_xmit 将数据报文发送给链路层驱动

 

posted @   codestacklinuxer  阅读(2072)  评论(0编辑  收藏  举报
编辑推荐:
· AI与.NET技术实操系列:基于图像分类模型对图像进行分类
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
阅读排行:
· 25岁的心里话
· 闲置电脑爆改个人服务器(超详细) #公网映射 #Vmware虚拟网络编辑器
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· 零经验选手,Compose 一天开发一款小游戏!
· 一起来玩mcp_server_sqlite,让AI帮你做增删改查!!
点击右上角即可分享
微信分享提示