analysis on tcp ip protocol stack

15
Linux TCP/IP 协协协协协 徐徐徐 CCNT, ZJU 06/07/22

Upload: yueshen-xu

Post on 27-Jan-2015

121 views

Category:

Technology


11 download

DESCRIPTION

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.

TRANSCRIPT

Page 1: Analysis on tcp ip protocol stack

Linux TCP/IP 协议栈分析

徐悦甡

CCNT, ZJU04/10/23

Page 2: Analysis on tcp ip protocol stack

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

04/10/23 CCNT, ZJU

应用层

传输层

网际层

网络接入层

系统调用

BSD Socket层

Inet Socket 层

IP 层

硬件接口层

Page 3: Analysis on tcp ip protocol stack

Linux协议栈概述(二)

04/10/23 CCNT, ZJU

数据发送部分与接收部分相对应 仅仅是数据流向不同 本次以数据发送端为例

与 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 目录下。

应用层

传输层

网际层

网络接入层

系统调用

BSD Socket层

Inet Socket 层

IP 层

硬件接口层

write

read

Page 4: Analysis on tcp ip protocol stack

Linux中数据发送总流程

04/10/23 CCNT, ZJU

write sendtosend

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

Page 5: Analysis on tcp ip protocol stack

应用层—— sys_sendto

sendto&send 只是 glibc 函数库中封装的函数,最终都会调用到内核函数 sys_sendto

04/10/23 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; };

Page 6: Analysis on tcp ip protocol stack

应用层—— sys_sendto

具体的填充过程

04/10/23 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;    };

Page 7: Analysis on tcp ip protocol stack

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&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 的命名空间指针

04/10/23 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;};

Page 8: Analysis on tcp ip protocol stack

BSD Socket层(二) —— sock_create

__sock_create

04/10/23 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 进行检查,查看是否超出正常范围

Page 9: Analysis on tcp ip protocol stack

BSD Socket层(三) —— sock_create

申请一个 socket node

04/10/23 CCNT, ZJU

 sock = sock_alloc();    if (!sock) {        if (net_ratelimit())            printk(KERN_WARNING “socket: no more sockets\n”);        return -ENFILE;      }

转入 sock_alloc

Page 10: Analysis on tcp ip protocol stack

BSD Socket层(四) —— sock_create

sock_alloc

04/10/23 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 的统计计数

Page 11: Analysis on tcp ip protocol stack

BSD Socket层(五) —— sock_create

通过 RCU 机制,获得 pf 对应的 net_families 中的指针

04/10/23 CCNT, ZJU

  rcu_read_lock();    pf = rcu_dereference(net_families[family]);   ……    rcu_read_unlock();

RCU 机制 读 - 拷贝 - 更新( read-copy-update ) , 被 2.6 内核正式引入。是

为了保护在多数情况下被多个 CPU 读的数据结构而设计的一种同步技术,允许多个读者和写者并发执行,不利用传统意义上的锁机制

Page 12: Analysis on tcp ip protocol stack

BSD Socket层(六) —— sock_create

通过函数指针调用用户指定协议簇中的函数去创建 socket

04/10/23 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 )对应的协议域定义

Page 13: Analysis on tcp ip protocol stack

硬件接口层(一)—— dev_queue_xmit

dev_queue_xmit

发送 sk_buff, 将其加入到 driver 的 queue 中 可以认为是 TCP/IP 协议栈中发送数据的最后一个函数

04/10/23 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 的处理函数,刚使用该流量控制对象发送数据包

Page 14: Analysis on tcp ip protocol stack

硬件接口层(二)—— dev_queue_xmit

dev_queue_xmit

04/10/23 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直接负责发送

Page 15: Analysis on tcp ip protocol stack

Q&AQ&A

04/10/23 Middleware, CCNT, ZJU