Socket 源码分析
我们使用 socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ALL)); 语句创建了一个 socket ,那么实际上发生了什么呢?
从下面的注释可以看的出来,之前是通过调用 sys_socketcall 再跳转, 现在是直接调用 sys_socket 函数。
下面的宏就是完成 sys_socket 到 SYSCALL_DEFINE3 宏包围的函数的调用关系。如果一大堆宏比较麻烦,可以直接参考 -->Linux系统调用之SYSCALL_DEFINE 的分析。
/include/linux/syscalls.h /* obsolete: net/socket.c */ asmlinkage long sys_socketcall(int call, unsigned long __user *args); /* net/socket.c */ asmlinkage long sys_socket(int, int, int); asmlinkage long sys_socketpair(int, int, int, int __user *); asmlinkage long sys_bind(int, struct sockaddr __user *, int); asmlinkage long sys_listen(int, int); asmlinkage long sys_accept(int, struct sockaddr __user *, int __user *); //omit... #define SYSCALL_DEFINEx(x, sname, ...) \ SYSCALL_METADATA(sname, x, __VA_ARGS__) \ __SYSCALL_DEFINEx(x, sname, __VA_ARGS__) #define __PROTECT(...) asmlinkage_protect(__VA_ARGS__) /* * The asmlinkage stub is aliased to a function named __se_sys_*() which * sign-extends 32-bit ints to longs whenever needed. The actual work is * done within __do_sys_*(). */ #ifndef __SYSCALL_DEFINEx #define __SYSCALL_DEFINEx(x, name, ...) \ __diag_push(); \ __diag_ignore(GCC, 8, "-Wattribute-alias", \ "Type aliasing is used to sanitize syscall arguments");\ asmlinkage long sys##name(__MAP(x,__SC_DECL,__VA_ARGS__)) \ __attribute__((alias(__stringify(__se_sys##name)))); \ ALLOW_ERROR_INJECTION(sys##name, ERRNO); \ static inline long __do_sys##name(__MAP(x,__SC_DECL,__VA_ARGS__));\ asmlinkage long __se_sys##name(__MAP(x,__SC_LONG,__VA_ARGS__)); \ asmlinkage long __se_sys##name(__MAP(x,__SC_LONG,__VA_ARGS__)) \ { \ long ret = __do_sys##name(__MAP(x,__SC_CAST,__VA_ARGS__));\ __MAP(x,__SC_TEST,__VA_ARGS__); \ __PROTECT(x, ret,__MAP(x,__SC_ARGS,__VA_ARGS__)); \ return ret; \ } \ __diag_pop(); \ static inline long __do_sys##name(__MAP(x,__SC_DECL,__VA_ARGS__)) #endif /* __SYSCALL_DEFINEx */ #define SYSCALL_DEFINE1(name, ...) SYSCALL_DEFINEx(1, _##name, __VA_ARGS__) #define SYSCALL_DEFINE2(name, ...) SYSCALL_DEFINEx(2, _##name, __VA_ARGS__) #define SYSCALL_DEFINE3(name, ...) SYSCALL_DEFINEx(3, _##name, __VA_ARGS__) #define SYSCALL_DEFINE4(name, ...) SYSCALL_DEFINEx(4, _##name, __VA_ARGS__) #define SYSCALL_DEFINE5(name, ...) SYSCALL_DEFINEx(5, _##name, __VA_ARGS__) #define SYSCALL_DEFINE6(name, ...) SYSCALL_DEFINEx(6, _##name, __VA_ARGS__)
关于 SYSCALL_DEFINE 解释比较详细的一篇文章
到此为止,用户 调用 socket 已经进行到了内核中此处的函数
代码位置: net/socket.c
SYSCALL_DEFINE3(socket, int, family, int, type, int, protocol) { int retval; struct socket *sock; int flags; //Omit... flags = type & ~SOCK_TYPE_MASK; if (flags & ~(SOCK_CLOEXEC | SOCK_NONBLOCK)) return -EINVAL; type &= SOCK_TYPE_MASK; if (SOCK_NONBLOCK != O_NONBLOCK && (flags & SOCK_NONBLOCK)) flags = (flags & ~SOCK_NONBLOCK) | O_NONBLOCK; // 可以看出就是这里进行的 socket 的创建工作,接下来重点对这个代码进行分析 retval = sock_create(family, type, protocol, &sock); if (retval < 0) goto out; retval = sock_map_fd(sock, flags & (O_CLOEXEC | O_NONBLOCK)); if (retval < 0) goto out_release; out: /* It may be already another descriptor 8) Not kernel problem. */ return retval; out_release: sock_release(sock); return retval; } sock_create 实际上是调用了 __socket_create int sock_create(int family, int type, int protocol, struct socket **res) { return __sock_create(current->nsproxy->net_ns, family, type, protocol, res, 0); } int __sock_create(struct net *net, int family, int type, int protocol, struct socket **res, int kern) { int err; struct socket *sock; const struct net_proto_family *pf; /* * Check protocol is in range */ if (family < 0 || family >= NPROTO) return -EAFNOSUPPORT; if (type < 0 || type >= SOCK_MAX) return -EINVAL; /* Compatibility. This uglymoron is moved from INET layer to here to avoid deadlock in module load. */ if (family == PF_INET && type == SOCK_PACKET) { pr_info_once("%s uses obsolete (PF_INET,SOCK_PACKET)\n", current->comm); family = PF_PACKET; } /* * Allocate the socket and allow the family to set things up. if * the protocol is 0, the family is instructed to select an appropriate * default. */ // 申请一个与 inode 绑定的 socket 。 sock = sock_alloc(); if (!sock) { net_warn_ratelimited("socket: no more sockets\n"); return -ENFILE; /* Not exactly a match, but its the closest posix thing */ } sock->type = type; rcu_read_lock(); // 从 RCU 中找到协议对应的数据 pf = rcu_dereference(net_families[family]); err = -EAFNOSUPPORT; if (!pf) goto out_release; /* Now protected by module ref count */ rcu_read_unlock(); // 调用其中的 create 函数 err = pf->create(net, sock, protocol, kern); if (err < 0) goto out_module_put; // Omit... *res = sock; return 0; // Omit... }
其中 net_families[family] 这个结构数组的定义可以下面,其中注释中已经说了,所有协议都将被注册到这个数组中。所以从 rcu 中找到的就是这个结构,调用的就是这个结构中的 create 函数。
/* * The protocol list. Each protocol is registered in here. */ static const struct net_proto_family __rcu *net_families[NPROTO] __read_mostly; struct net_proto_family { int family; int (*create)(struct net *net, struct socket *sock, int protocol, int kern); struct module *owner; };
回到开始,我们创建 socket 使用的 family 参数是 AP_PACKET, 于是找到对应的 net_proto_family 结构。
位置: net/packet/af_packet.c
static const struct net_proto_family packet_family_ops = { .family = PF_PACKET, .create = packet_create, .owner = THIS_MODULE, };
这个是kernel中队 sock 结构的解释。接下来继续分析 packet_create 函数。
// struct sock - network layer representation of sockets static int packet_create(struct net *net, struct socket *sock, int protocol, int kern) { struct sock *sk; struct packet_sock *po; __be16 proto = (__force __be16)protocol; /* weird, but documented */ int err; if (!ns_capable(net->user_ns, CAP_NET_RAW)) return -EPERM; if (sock->type != SOCK_DGRAM && sock->type != SOCK_RAW && sock->type != SOCK_PACKET) return -ESOCKTNOSUPPORT; sock->state = SS_UNCONNECTED; /* unconnected to any socket */ err = -ENOBUFS; //根据参数创建 sock 对象。 sk = sk_alloc(net, PF_PACKET, GFP_KERNEL, &packet_proto, kern); if (sk == NULL) goto out; // 这里面有关于套接字的各种操作,比如 bind、accept 等。 sock->ops = &packet_ops; if (sock->type == SOCK_PACKET) sock->ops = &packet_ops_spkt; // 经过前面的初始化过程后,此处将 sock 结构 与 socket 发生关联(将 socket 绑定到 sock 上),并初始化 sock 结构。 sock_init_data(sock, sk); po = pkt_sk(sk); sk->sk_family = PF_PACKET; po->num = proto; // 将协议添加到此处 po->xmit = dev_queue_xmit; //设置发送数据包函数 err = packet_alloc_pending(po); if (err) goto out2; packet_cached_dev_reset(po); sk->sk_destruct = packet_sock_destruct; // 销毁数据包 sk_refcnt_debug_inc(sk); /* * Attach a protocol block */ spin_lock_init(&po->bind_lock); mutex_init(&po->pg_vec_lock); po->rollover = NULL; // 指定 hook 的函数 po->prot_hook.func = packet_rcv; // 如果参数使用的是 SOCK_PACKET 那么函数就要使用 packet_rcv_spkt if (sock->type == SOCK_PACKET) po->prot_hook.func = packet_rcv_spkt; po->prot_hook.af_packet_priv = sk; // 这里就可以看出指定的参数中的协议类型的作用,将会将 hook 的类型设置为协议类型 if (proto) { po->prot_hook.type = proto; register_prot_hook(sk); // 这个函数还可以分析一下 } mutex_lock(&net->packet.sklist_lock); sk_add_node_rcu(sk, &net->packet.sklist); mutex_unlock(&net->packet.sklist_lock); preempt_disable(); sock_prot_inuse_add(net, &packet_proto, 1); preempt_enable(); return 0; out2: sk_free(sk); out: return err; } ``` 这样就创建出了一个sock结构,客户端的句柄实际上在内核中就是这个的对应对应关系。接下来的 bind、 send 等函数就是调用 packet_ops 结构中的函数。 ## 拓展 [关于 Linux RCU 的详细介绍](http://www2.rdrop.com/users/paulmck/RCU/) [Linux网络之设备接口层:发送数据包流程dev_queue_xmit](https://blog.csdn.net/wdscq1234/article/details/51926808)