Analysis on tcp ip protocol stack
Upcoming SlideShare
Loading in...5
×
 

Analysis on tcp ip protocol stack

on

  • 865 views

This is my summary report for the operating system course. Of course, I am just one of the three in the group. I hope it is of help for you.

This is my summary report for the operating system course. Of course, I am just one of the three in the group. I hope it is of help for you.

Statistics

Views

Total Views
865
Views on SlideShare
865
Embed Views
0

Actions

Likes
1
Downloads
15
Comments
0

0 Embeds 0

No embeds

Accessibility

Categories

Upload Details

Uploaded via as Microsoft PowerPoint

Usage Rights

© All Rights Reserved

Report content

Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

Cancel
  • Full Name Full Name Comment goes here.
    Are you sure you want to
    Your message goes here
    Processing…
Post Comment
Edit your comment

    Analysis on tcp ip protocol stack Analysis on tcp ip protocol stack Presentation Transcript

    • Linux TCP/IP 协议栈分析 徐悦甡 CCNT, ZJU 11/26/11
    • Linux 协议栈概述(一)
      • 与 TCP/IP 分层模型相对应
      • BSD socket 层 
      • 屏蔽协议差异,提供通用接口,每个 socket 在内核中以 struct socket 结构体现,这一部分的文件主要有: /net/socket.c /net/protocols.c etc
      • INET socket 层
      • 当用于 TCP/IP 协议栈时,即建立了 AF_INET 形式的 socket 时,需要额外参数的支持,于是就有了 struct sock 结构,文件主要有: /net/ipv4/protocol.c /net/ipv4/af_inet.c /net/core/sock.c etc
      11/26/11 CCNT, ZJU 应用层 传输层 网际层 网络接入层 系统调用 BSD Socket 层 Inet Socket 层 IP 层 硬件接口层
    • Linux 协议栈概述(二)
      • 与 TCP/IP 分层模型相对应
      • IP 层
      • 处理网络层的操作,网络层用 struct packet_type 结构表示。文件主要有: /net/ipv4/ip_forward.c ip_fragment.c ip_input.c ip_output.c
      • 硬件接口层
      • 每个网络设备以 struct net_device 表示,通用的处理在 /net/core/dev.c 中,驱动程序都在 /driver/net 目录下。
      11/26/11 CCNT, ZJU
      • 数据发送部分与接收部分相对应
      • 仅仅是数据流向不同
      • 本次以数据发送端为例
      应用层 传输层 网际层 网络接入层 系统调用 BSD Socket 层 Inet Socket 层 IP 层 硬件接口层 write read
    • Linux 中数据发送总流程 11/26/11 CCNT, ZJU write sendto send sys_write sys_send sock_create sys_sendto sock_sendmsg inet_sendmsg tcp_sendmsg tcp_send_skb tcp_transmit_skb ip_queue_xmit ip_queue_xmit2 ip_output ip_finish_output ip_finish_output2 dev_queue_xmit dev_hard_start_xmit 应用层 BSD Socket 层 Inet Socket 层 IP 层 硬件接口层 /net/ipv4; /net/core
    • 应用层 —— sys_sendto
      • sendto&send
      • 只是 glibc 函数库中封装的函数,最终都会调用到内核函数 sys_sendto
      11/26/11 CCNT, ZJU asmlinkage long sys_sendto( int  fd, void __user *buff, size_t len, unsigned flags, struct sockaddr __user *addr, int addr_len) fd  : socket 文件描述符 buff :指向需要发送的数据 len :需要发送的数据的长度 flags :标志位 addr :数据报文要发送的对方端点的地址信息 addr_len :地址信息的长度
      • 核心工作:填充 msghdr 结构
      struct  msghdr  {     void             *msg_name;     int              msg_namelen;      struct iovec     *msg_iov;      __kernel_size_t -msg_iovlen;      void             *msg_control;      __kernel_size_t -msg_controllen;      unsigned         msg_flags; };
    • 应用层 —— sys_sendto
      • 具体的填充过程
      11/26/11 CCNT, ZJU iov.iov_base = buff; iov.iov_len = len; msg.msg_name = NULL; msg.msg_iov = &iov; msg.msg_iovlen = 1; …… msg.msg_name = NULL; msg.msg_namelen = 0; if (addr) {        ……    msg.msg_name = address;    msg.msg_namelen = addr_len; }   Step1 : msg_name/msg_namelen 是数据报文要发向的对端的地址信息,即 sendto 系统调用中的 addr 和 addr_len) 。当使用 send 时,它们的值为 NULL 和 0 Step2 :填充 iovec ,存放待发送数据的缓冲区, iov_base 指向缓冲区的起始地址, iov_len 是缓冲区的长度,指向 length Step3 : msghdr->msg_iovlen , msg_ iovlen 是缓冲区的数量,对于 sendto 和 send 来讲, msg_iovlen 都是 1 struct iovec {         void __user     *iov_base;         __kernel_size_t iov_len;     };
    • BSD Socket 层(一) —— sock_create
      • sock_create/__sock_create
      • 原型: int sock_create(int family, int type, int protocol, struct socket )
      • 实例: sock_create(AF_INET, SOCK_DGRAM, IPPROTO_IP, &sock )
      • int sock_create(int family, int type, int protocol, struct socket **res) { return __sock_create(current -> nsproxy ->net_ns ,  family ,  type ,  protocol ,  res ,  0 );
      • } : 真正的工作由 __sock_create 来做
      • current 是指向当前 task 的指针, task 的类型为 struct task_struct
      • nsproxy 是它的一个成员变量,是 task 的命名空间指针
      11/26/11 CCNT, ZJU struct nsproxy {     struct uts_namespace *uts_ns;     struct ipc_namespace *ipc_ns;     struct mnt_namespace *mnt_ns;     struct pid_namespace *pid_ns;     struct net      *net_ns; };
    • BSD Socket 层(二) —— sock_create
      • __sock_create
      11/26/11 CCNT, ZJU static int __sock_create(struct net *net, int family, int type, int protocol,struct  socket * *res, int kern){ ……      if (family < 0 || family >= NPROTO)         return -EAFNOSUPPORT;     if (type < 0 || type >= SOCK_MAX)         return -EINVAL; …… }
      • 对 family 和 type 进行检查,查看是否超出正常范围
    • BSD Socket 层(三) —— sock_create
      • 申请一个 socket node
      11/26/11 CCNT, ZJU   sock = sock_alloc();     if (!sock) {         if (net_ratelimit())             printk(KERN_WARNING “socket: no more socketsn”);         return -ENFILE;       }
      • 转入 sock_alloc
    • BSD Socket 层(四) —— sock_create
      • sock_alloc
      11/26/11 CCNT, ZJU static struct socket *sock_alloc(void) { ……      inode = new_inode(sock_mnt->mnt_sb); ……     sock = SOCKET_I(inode);     ……     inode->i_mode = S_IFSOCK | S_IRWXUGO;     inode->i_uid = current_fsuid();     inode->i_gid = current_fsgid();   percpu_add(sockets_in_use, 1);     return sock; }
      • sock_mnt 是一个全局变量,在 sock_init 中被初始化的,并挂载到 VFS 层上
      • Step1 : 从 sock_mnt->mnt_sb 即 socket 的超级块中申请一个节点
      • Step2 :通过 SOCKET_I 宏,取得 inode 对应的 socket 的地址
      • Step3 :设置 inode 的 mode , uid , gid
      • Step4 :增加 sockets_in_use 的统计计数
    • BSD Socket 层(五) —— sock_create
      • 通过 RCU 机制,获得 pf 对应的 net_families 中的指针
      11/26/11 CCNT, ZJU   rcu_read_lock();     pf = rcu_dereference(net_families[family]);    ……     rcu_read_unlock();
      • RCU 机制
      • 读 - 拷贝 - 更新( read-copy-update ) , 被 2.6 内核正式引入。是为了保护在多数情况下被多个 CPU 读的数据结构而设计的一种同步技术,允许多个读者和写者并发执行,不利用传统意义上的锁机制
    • BSD Socket 层(六) —— sock_create
      • 通过函数指针调用用户指定协议簇中的函数去创建 socket
      11/26/11 CCNT, ZJU   err = pf->create(net, sock, protocol, kern);
      • 对于 TCP/IP 来说, family 是 PF_INET
      • PF_INET ( linux/net/ipv4/af_inet.c )对应的协议域定义
      static const struct net_proto_family inet_family_ops = {     .family = PF_INET,     .create =  inet_create,     .owner    = THIS_MODULE, };
      • 对于 TCP/IP 来说, family 是 PF_INET
      • PF_INET ( linux/net/ipv4/af_inet.c )对应的协议域定义
    • 硬件接口层(一) —— dev_queue_xmit
      • dev_queue_xmit
      • 发送 sk_buff, 将其加入到 driver 的 queue 中
      • 可以认为是 TCP/IP 协议栈中发送数据的最后一个函数
      11/26/11 CCNT, ZJU int dev_queue_xmit(struct sk_buff *skb){      struct net_device *dev = skb->dev; ……      txq = dev_pick_tx(dev, skb);        if (q->enqueue) {          rc = __dev_xmit_skb(skb, q, dev, txq);         ……      } Step1 :获得指向发送设备的指针,并得到发送设备的发送队列 Step2 :获得出口流量控制对象的结构 Step3 :如果该设备有 enqueue 的处理函数,刚使用该流量控制对象发送数据包
    • 硬件接口层(二) —— dev_queue_xmit
      • dev_queue_xmit
      11/26/11 CCNT, ZJU     if (dev->flags & IFF_UP) {          int cpu = smp_processor_id();           if (txq->xmit_lock_owner != cpu) {              HARD_TX_LOCK(dev, txq, cpu);              if (!netif_tx_queue_stopped(txq)) {                 rc = dev_hard_start_xmit(skb, dev, txq); …… Step1 : 获取当前 CPU 的 id Step3 :判断别的 CPU 是否正在使用该设备,如果没有,则尝试获得锁,从而获取 device 的使用权 Step2 :由 dev_hard_start_xmit 直接负责发送
      • Q&A
      11/26/11 Middleware, CCNT, ZJU