Linux构造ip包并发送,linux中数据包的发送——传输层到链路层

传输层到链路层

——lvyilong316

这里以TCP为例,讲述报文是如何从传输层进入链路层的。在TCP中将包打包成IP数据报的方法根据TCP段类型的不同而有多种接口。其中最常用的就是ip_queue_xmit()。我们就从这个接口开始。注:以下代码都去掉了不重要的一些逻辑。

lip_queue_xmit

net/ipv4/ip_output.c

点击(此处)折叠或打开

int ip_queue_xmit(struct sk_buff *skb, int ipfragok)

{

struct sock *sk = skb->sk;

struct inet_sock *inet = inet_sk(sk);

struct ip_options *opt = inet->opt;

struct rtable *rt;

struct iphdr *iph;

/*……*/

/* Make sure we can route this packet. */

rt = (struct rtable )__sk_dst_check(sk, 0); /*取出sk中缓存的“路由缓存”*/

if (rt == NULL) { /*如果没有缓存“路由缓存”,则要查找路由缓存*/

__be32 daddr;

daddr = inet->daddr;

{

struct flowi fl = { .oif = sk->sk_bound_dev_if,

.mark = sk->sk_mark,

.nl_u = { .ip4_u =

{ .daddr = daddr,

.saddr = inet->saddr,

.tos = RT_CONN_FLAGS(sk) } },

.proto = sk->sk_protocol,

.flags = inet_sk_flowi_flags(sk),

.uli_u = { .ports =

{ .sport = inet->sport,

.dport = inet->dport } } };

if (ip_route_output_flow(sock_net(sk), &rt, &fl, sk, 0))

goto no_route;

}

sk_setup_caps(sk, &rt->u.dst);

}

skb_dst_set(skb, dst_clone(&rt->u.dst));

packet_routed:

/* OK, we know where to send it, allocate and build IP header. */

/*这里省略了根据查找出得路由缓存设置ip头部字段,包括源、目的地址、ttl、是否允许分片等标示*/

return ip_local_out(skb);

}

struct rtable标示路由缓存。下面主要看ip_route_output_flow这个函数,这个函数是用来查找路由缓存的(注意不是查找路由表的,只有路由缓存查找不命中时才会查找路由表)。另外注意查找路由缓存的key是struct flowi结构,而不单单是目的地址。

lip_route_output_flow

net/ipv4/route.c

点击(此处)折叠或打开

/**

*rp:当查找成功时返回查找得到的路由缓存项

*flp:用于查找路由缓存的struct flowi结构

*sk,flags:支持IPSec策略处理,此处不讨论

*/

int ip_route_output_flow(struct net *net, struct rtable **rp, struct flowi *flp,

struct sock *sk, int flags)

{

int err;

if ((err = __ip_route_output_key(net, rp, flp)) != 0)

return err;

return 0;

}

这个函数进一步调用了__ip_route_output_key进行路由缓存的查找,下面看__ip_route_output_key。

l__ip_route_output_key

net/ipv4/route.c

点击(此处)折叠或打开

int __ip_route_output_key(struct net *net, struct rtable **rp,

const struct flowi *flp)

{

unsigned hash;

struct rtable *rth;

if (!rt_caching(net))

goto slow_output;

/*取得路由缓存对应的hash桶*/

hash = rt_hash(flp->fl4_dst, flp->fl4_src, flp->oif, rt_genid(net));

rcu_read_lock_bh();

for (rth = rcu_dereference(rt_hash_table[hash].chain); rth;

rth = rcu_dereference(rth->u.dst.rt_next)) {

/*根据flowi结构查找路由缓存,如果找着到则通过rp返回,查找比较过程省略*/

}

RT_CACHE_STAT_INC(out_hlist_search);

}

rcu_read_unlock_bh();

slow_output:

/*如果路由缓存查找不到,则查找路由表*/

return ip_route_output_slow(net, rp, flp);

}

我们还是按照最坏的结果分析,如果路由缓存没有查找到,则调用ip_route_output_slow查处路由表。

lip_route_output_slow

net/ipv4/route.c

点击(此处)折叠或打开

static int ip_route_output_slow(struct net *net, struct rtable **rp,

const struct flowi *oldflp)

{

u32 tos = RT_FL_TOS(oldflp);

struct flowi fl = { .nl_u = { .ip4_u =

{ .daddr = oldflp->fl4_dst,

.saddr = oldflp->fl4_src,

.tos = tos & IPTOS_RT_MASK,

.scope = ((tos & RTO_ONLINK) ?

RT_SCOPE_LINK :

RT_SCOPE_UNIVERSE),

} },

.mark = oldflp->mark,

.iif = net->loopback_dev->ifindex,

.oif = oldflp->oif };

struct fib_result res; /*存放查找结果*/

int err;

/* fib_lookup 实现路由查找的核心逻辑*/

if (fib_lookup(net, &fl, &res)) {

/*查找失败的处理。略*/

}

/*根据查找路由表的结果构建路由缓存*/

make_route:

err = ip_mkroute_output(rp, &res, &fl, oldflp, dev_out, flags);

}

查找路由表的逻辑主要在fib_lookup中实现,我们这里不在去分析,因为与发送逻辑无关,并且设

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值