tcp/ip协议栈--socket API 之bind()

来源:互联网 发布:金融管理信息系统软件 编辑:程序博客网 时间:2024/06/04 19:06

0x01 缘由

     上篇博文已经学习了socket() API,但是还不清楚创建的socket等相关结构如何使用。上篇文章仅仅创建了相关存储结构和文件描述符sockfd,并没有表示任何地址和端口。

0x02 bind()介绍

    

0x03 单步跟踪分析

     guest主机上运行server.c,此函数是通用的服务端模式,其中调用bind()函数,同时在内核源码通用处理socket操作的函数中设置断点。
     

3.1 SYSCALL_DEFINE2

  /* *    系统调用向量 */SYSCALL_DEFINE2(socketcall, int, call, unsigned long __user *, args){    unsigned long a[6];    unsigned long a0, a1;    int err;    unsigned int len;    if (call < 1 || call > SYS_ACCEPT4)        return -EINVAL;    len = nargs[call];    if (len > sizeof(a))        return -EINVAL;    /* 用户空间复制相关参数 */    if (copy_from_user(a, args, len))        return -EFAULT;    audit_socketcall(nargs[call] / sizeof(unsigned long), a);    a0 = a[0];    a1 = a[1];    /* 根据call子调用号,来处理socket相关调用状态。*/    switch (call) {     ......    case SYS_BIND:        err = sys_bind(a0, (struct sockaddr __user *)a1, a[2]); 设置断点,传入相关参数sockfd,地址端口等。        break;    .......    default:        err = -EINVAL;        break;    }    return err;}

3.2 SYSCALL_DEFINE3

/* 第一个参数bind */SYSCALL_DEFINE3(bind, int, fd, struct sockaddr __user *, umyaddr, int, addrlen){    struct socket *sock;    struct sockaddr_storage address; //内核态存储结构    int err, fput_needed;    /* 通过fd查找之前创建的socket,sock等结构,此处就知道与上篇文章如何关联的。*/    sock = sockfd_lookup_light(fd, &err, &fput_needed);    if (sock) {        /* 将地址从用户态拷贝到内核态 */        err = move_addr_to_kernel(umyaddr, addrlen, (struct sockaddr *)&address);        if (err >= 0) {            /* 相关安全机制 SELInux相关 */            err = security_socket_bind(sock,                           (struct sockaddr *)&address,                           addrlen);            if (!err)                /* 调用初始化注册的相关回调操作函数。此处为tcp协议,则调用inet_bind*/                err = sock->ops->bind(sock,                              (struct sockaddr *)                              &address, addrlen);        }        fput_light(sock->file, fput_needed);    }    return err;}

3.3 inet_bind

int inet_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len){    struct sockaddr_in *addr = (struct sockaddr_in *)uaddr;    struct sock *sk = sock->sk;    struct inet_sock *inet = inet_sk(sk);    unsigned short snum;    int chk_addr_ret;    int err;    /* 调用协议本身的 bind函数(即 struct sock 的成员 sk_prot->bind),但 UDP 和 TCP 协议本身不提供 bind 函数。 */    if (sk->sk_prot->bind) {        err = sk->sk_prot->bind(sk, uaddr, addr_len);        goto out;    }    err = -EINVAL;    /* 地址长度检查 */    if (addr_len < sizeof(struct sockaddr_in))        goto out;    /* 查找地址类型,就像系统中只存在“dev”一样。如果on_dev为NULL,则会考虑所有接口。在路由中检查IP地址类型,单播、多播还是广播*/    chk_addr_ret = inet_addr_type(sock_net(sk), addr->sin_addr.s_addr);    err = -EADDRNOTAVAIL;    /* sysctl_ip_nonlocal_bind表示是否允许绑定非本地的IP地址     * inet->freebind表示是否允许绑定非主机地址。     * 这里需要允许绑定非本地地址,除非是发送给自己、多播或广播。     */    if (!sysctl_ip_nonlocal_bind &&        !(inet->freebind || inet->transparent) &&        addr->sin_addr.s_addr != htonl(INADDR_ANY) &&        chk_addr_ret != RTN_LOCAL &&        chk_addr_ret != RTN_MULTICAST &&        chk_addr_ret != RTN_BROADCAST)        goto out;    /*端口地址,网络序转主机序*/    snum = ntohs(addr->sin_port);    err = -EACCES;    /*端口合理范围检查,0-1023 号端口是只有超级用户才有权限执行绑定的。*/    if (snum && snum < PROT_SOCK && !capable(CAP_NET_BIND_SERVICE))        goto out;    /* 我们保持一个地址对:接收地址和发送地址。接收地址是通过hash查询,源地址是来作为传输。*/    /* 锁结构*/    lock_sock(sk);    /* 如果套接字不在初始状态TCP_CLOSE,或者已经绑定端口了,则出错。一个socket最多可以绑定一个端口,而一个端口则可能被多个socket共用。 */    err = -EINVAL;    if (sk->sk_state != TCP_CLOSE || inet->num)        goto out_release_sock;    /*地址相关赋值*/    inet->rcv_saddr = inet->saddr = addr->sin_addr.s_addr;    if (chk_addr_ret == RTN_MULTICAST || chk_addr_ret == RTN_BROADCAST)        inet->saddr = 0;  /* Use device */    /*这儿确定能够绑定,此处为tcp协议,调用int inet_csk_get_port(struct sock *sk, unsigned short snum)端口可用返回0。 */    if (sk->sk_prot->get_port(sk, snum)) {        inet->saddr = inet->rcv_saddr = 0;        err = -EADDRINUSE;        goto out_release_sock;    }    /* inet_rcv_saddr表示绑定的地址,接收数据时用于查找socket */     if (inet->rcv_saddr)        sk->sk_userlocks |= SOCK_BINDADDR_LOCK; /* 表示绑定了本地地址 */     if (snum)        sk->sk_userlocks |= SOCK_BINDPORT_LOCK; /* 表示绑定了本地端口 */     inet->sport = htons(inet->num);/* 绑定端口转网络序 */     inet->daddr = 0;    inet->dport = 0;    sk_dst_reset(sk);    err = 0;out_release_sock:    release_sock(sk);out:    return err;}

3.4 inet_csk_get_port

     当指定端口时绑定到指定端口,当参数为0时随机选出一个可用端口。
     此函数的介绍传送到前辈:http://blog.csdn.net/zhangskd/article/details/14170013

0x04 总结

     此处将socket和bind函数串联起来了,相当于相关结构已经建立。下一章讲解listen调用。(学习过程,大神勿喷,多指点)
     
原创粉丝点击