net/socket.c
调用send,调用__sys_sendto
应用层将待发送的数据放在自己申请的buff中
INETsocket层将数据放在msghdr{}中
INETsocket以下的层将数据放在sk_buff{}中
在sendto的系统调用中,首先找到BSD socket,再调用send_message。
sendmessage里面执行socket->ops->sendmsg。这里补充,执行的是sock的prot的sendmessage,即真正的sendmessage
比如af_inet.c / inet_sendmessage,其中inet_butobind会自动分配端口号,所以客户端可以socket之后直接send
以raw_socket为例
在路由系统中游历.当要发送一个报文时,必定要查询发送接口,这个过程被linux分为三个步骤,第一个时查询路由cache,第二个是查询FIB表,第三步是将查询结果填入路由cache中以便将来查询。
路由cache可以看作是FIB的子集,它是用来优化已知目的地址的已打开socket的快速路由的。路由cache的实现基于通用的目的地址cache架构,路由缓存是rt_hash_table,它由hash表组成,每一个表项包含路由表项rtable。这个表可以用简单的key完成快速搜索。hash表的实现允许冲突,因为每一个hash表位置可以包含多个路由。IP中的路由cache是一个hash桶的实例。在此数组中每一个单元包含一个指向路由表的链,每个匹配某目的地址的路由放在一个由链表指向的连接列表中。rtable的结构如下:include/net/route.h
例如在raw_sendmsg中,通过ip_route_output_flow获取rtable,输入flowi用于标识操作系统内部不同业务流。具体的实现在ip_route_output_flow,在rt_hash_table中,先使用hash_code找到一个对应的slot既一个路由表项的链表,然后通过flowi遍历查找路由。每个rtable第一部分包含一个目的cache表项结构,dst_entry,里面包含目的设备,设备操作,输入和输出操作。
在下面的算法中,具体查找cache,ip_route_output_key_hash_rcu
路由cache也被linux内核消除
现在版本5.0直接就是查找fib,在此之前,还有一个查找IP地址对应的设备接口,这个函数也调用了fib_lookup函数
fib_lookup内部先获得fib表。
最后选择路径,在选择路径中,可能是本地地址或者远端主机(非同一子网的主机)
在fib_selet_path中,对于远端主机要是用默认的转发信息。
最好调用__mkroute_output,在这里面分配一个rtable,会修改一些必要的信息??暂不明确
到现在为止,到IP层已经完成,下面还要添加MAC层信息,解决ARP解析IP地址到MAC地址。邻居系统。
这些都完事之后,从ip_route_output_flow返回,继续执行发送报文过程,就是ip_push-pending_frames
在这里面首先将IP分片合成一个IP报文,然后发送出去,ip_send_skb->ip_local_out->__ip_local_out/dst_output。
调用skb->dst->output,这个在__mkroute_output中进行了赋值
这个output调用了ip_finish_output->ip_finish_output2->neigh_output
其中hh_cache是arp缓存的一部分,类似cache
其中n->output调用neigh_resolve_output,其中neight_event_send解释:。其中dev_queue_xmit才是真正到达了驱动程序那一层代码。
后面的介绍链路层的一些东西先跳过。
主机中又IP-MAC键值对,在linux中是通过rtable+dst_entry+hh_cache完成的,如果查不到,就发送ARP报文,仅限于局域网,因为无法穿过路由器。此外还有RARP。
ARP初始化在net/ipv4/af_inet.c中初始化arp_init。
其中初始化一个neigh_table,在linux内核中是用来包含和本机相连接的所有的邻居的数据结构,包含设备的硬件地址信息。
arp_tbl是一个缓存表。