标签:type 才有 rcu 组播 通过 ret realloc pac nbsp
不管是收到报文转发还是本机发送报文,最后都会调用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); }
单播:
/* * 对于单播数据包,目的路由缓存项中的输出接口是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)); }
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); }
/* 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 将数据报文发送给链路层驱动
标签:type 才有 rcu 组播 通过 ret realloc pac nbsp
原文地址:https://www.cnblogs.com/codestack/p/9292122.html