当前位置:网站首页>tcp_ Diag kernel related implementation 1 call hierarchy
tcp_ Diag kernel related implementation 1 call hierarchy
2022-04-23 15:05:00 【Mrpre】
tcp_diag Kernel related implementation
Preface
tcp_diag It's a kernel module , The purpose of this article is to sort out the calling relationship , If from the user state socket Call all the way to tcp_diag modular dump Out of all socket Of .
Roughly hierarchical relationship Summarized below :netlink layer ->sock_diag layer ->inet_diag layer ->tcp_diag
User status code
similar ss
The function code can be from https://man7.org/linux/man-pages/man7/sock_diag.7.html gain , But it just prints unix_socket .sdiag_family = AF_UNIX,
#include <errno.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <linux/netlink.h>
#include <linux/rtnetlink.h>
#include <linux/sock_diag.h>
#include <linux/unix_diag.h>
static int
send_query(int fd)
{
struct sockaddr_nl nladdr = {
.nl_family = AF_NETLINK
};
struct
{
struct nlmsghdr nlh;
struct unix_diag_req udr;
} req = {
.nlh = {
.nlmsg_len = sizeof(req),
.nlmsg_type = SOCK_DIAG_BY_FAMILY,
.nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP
},
.udr = {
.sdiag_family = AF_UNIX,
.udiag_states = -1,
.udiag_show = UDIAG_SHOW_NAME | UDIAG_SHOW_PEER
}
};
struct iovec iov = {
.iov_base = &req,
.iov_len = sizeof(req)
};
struct msghdr msg = {
.msg_name = &nladdr,
.msg_namelen = sizeof(nladdr),
.msg_iov = &iov,
.msg_iovlen = 1
};
for (;;) {
if (sendmsg(fd, &msg, 0) < 0) {
if (errno == EINTR)
continue;
perror("sendmsg");
return -1;
}
return 0;
}
}
static int
print_diag(const struct unix_diag_msg *diag, unsigned int len)
{
if (len < NLMSG_LENGTH(sizeof(*diag))) {
fputs("short response\n", stderr);
return -1;
}
if (diag->udiag_family != AF_UNIX) {
fprintf(stderr, "unexpected family %u\n", diag->udiag_family);
return -1;
}
unsigned int rta_len = len - NLMSG_LENGTH(sizeof(*diag));
unsigned int peer = 0;
size_t path_len = 0;
char path[sizeof(((struct sockaddr_un *) 0)->sun_path) + 1];
for (struct rtattr *attr = (struct rtattr *) (diag + 1);
RTA_OK(attr, rta_len); attr = RTA_NEXT(attr, rta_len)) {
switch (attr->rta_type) {
case UNIX_DIAG_NAME:
if (!path_len) {
path_len = RTA_PAYLOAD(attr);
if (path_len > sizeof(path) - 1)
path_len = sizeof(path) - 1;
memcpy(path, RTA_DATA(attr), path_len);
path[path_len] = '\0';
}
break;
case UNIX_DIAG_PEER:
if (RTA_PAYLOAD(attr) >= sizeof(peer))
peer = *(unsigned int *) RTA_DATA(attr);
break;
}
}
printf("inode=%u", diag->udiag_ino);
if (peer)
printf(", peer=%u", peer);
if (path_len)
printf(", name=%s%s", *path ? "" : "@",
*path ? path : path + 1);
putchar('\n');
return 0;
}
static int
receive_responses(int fd)
{
long buf[8192 / sizeof(long)];
struct sockaddr_nl nladdr;
struct iovec iov = {
.iov_base = buf,
.iov_len = sizeof(buf)
};
int flags = 0;
for (;;) {
struct msghdr msg = {
.msg_name = &nladdr,
.msg_namelen = sizeof(nladdr),
.msg_iov = &iov,
.msg_iovlen = 1
};
ssize_t ret = recvmsg(fd, &msg, flags);
if (ret < 0) {
if (errno == EINTR)
continue;
perror("recvmsg");
return -1;
}
if (ret == 0)
return 0;
if (nladdr.nl_family != AF_NETLINK) {
fputs("!AF_NETLINK\n", stderr);
return -1;
}
const struct nlmsghdr *h = (struct nlmsghdr *) buf;
if (!NLMSG_OK(h, ret)) {
fputs("!NLMSG_OK\n", stderr);
return -1;
}
for (; NLMSG_OK(h, ret); h = NLMSG_NEXT(h, ret)) {
if (h->nlmsg_type == NLMSG_DONE)
return 0;
if (h->nlmsg_type == NLMSG_ERROR) {
const struct nlmsgerr *err = NLMSG_DATA(h);
if (h->nlmsg_len < NLMSG_LENGTH(sizeof(*err))) {
fputs("NLMSG_ERROR\n", stderr);
} else {
errno = -err->error;
perror("NLMSG_ERROR");
}
return -1;
}
if (h->nlmsg_type != SOCK_DIAG_BY_FAMILY) {
fprintf(stderr, "unexpected nlmsg_type %u\n",
(unsigned) h->nlmsg_type);
return -1;
}
if (print_diag(NLMSG_DATA(h), h->nlmsg_len))
return -1;
}
}
}
int
main(void)
{
int fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_SOCK_DIAG);
if (fd < 0) {
perror("socket");
return 1;
}
int ret = send_query(fd) || receive_responses(fd);
close(fd);
return ret;
}
User status code - establish socket
nt fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_SOCK_DIAG);
Corresponding kernel Call path sys_socket -> netlink_create -> __netlink_create
Because the input parameter of user state is AF_NETLINK
,netlink The module has registered the corresponding net_proto_family
static const struct net_proto_family netlink_family_ops = {
.family = PF_NETLINK,
.create = netlink_create,
.owner = THIS_MODULE, /* for consistency 8) */
};
sock_register(&netlink_family_ops);
__netlink_create
There is only one thing we need to care about , Is the operation function sock->ops
, He decided when you were right netlink Of fd call send/recv Such cases , Actual kernel functions
static const struct proto_ops netlink_ops = {
.family = PF_NETLINK,
.owner = THIS_MODULE,
.release = netlink_release,
.bind = netlink_bind,
.connect = netlink_connect,
.socketpair = sock_no_socketpair,
.accept = sock_no_accept,
.getname = netlink_getname,
.poll = datagram_poll,
.ioctl = netlink_ioctl,
.listen = sock_no_listen,
.shutdown = sock_no_shutdown,
.setsockopt = netlink_setsockopt,
.getsockopt = netlink_getsockopt,
.sendmsg = netlink_sendmsg,
.recvmsg = netlink_recvmsg,
.mmap = sock_no_mmap,
.sendpage = sock_no_sendpage,
};
static int __netlink_create(struct net *net, struct socket *sock,
struct mutex *cb_mutex, int protocol,
int kern)
{
struct sock *sk;
struct netlink_sock *nlk;
sock->ops = &netlink_ops;
...
}
User status code - send out dump request
here , We use AF_INET
Replace... In the above example AF_UNIX
struct
{
struct nlmsghdr nlh;
struct inet_diag_req_v2 r;
} req = {
.nlh = {
.nlmsg_len = sizeof(req),
.nlmsg_type = SOCK_DIAG_BY_FAMILY,
.nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP
},
.r = {
.sdiag_family = AF_INET,
.idiag_states = ((1 << TCP_CLOSING + 1) - 1); //states to dump
}
};
sendmsg(fd, &msg, 0)
sendmsg Corresponding kernel call path sys_sendmsg -> netlink_sendmsg -> Find the corresponding kernel socket -> netlink_unicast_kernel -> nlk_sk(sk)->netlink_rcv
netlink_sendmsg Will be based on fd The type of , Use netlink_getsockbyportid
function , adopt NETLINK_SOCK_DIAG
, Find the corresponding kernel socket, This kernel socket Responsible for handling user status programs send The data of
static int __net_init diag_net_init(struct net *net)
{
struct netlink_kernel_cfg cfg = {
.groups = SKNLGRP_MAX,
.input = sock_diag_rcv,
.bind = sock_diag_bind,
.flags = NL_CFG_F_NONROOT_RECV,
};
// nlk_sk(sk)->netlink_rcv = cfg.input
net->diag_nlsk = netlink_kernel_create(net, NETLINK_SOCK_DIAG, &cfg);
return net->diag_nlsk == NULL ? -ENOMEM : 0;
}
So user status data , Because by NETLINK_SOCK_DIAG
Created socket, therefore First of all be sock_diag_rcv
Handle
Call path ->sock_diag_rcv->sock_diag_rcv_msg
static void sock_diag_rcv(struct sk_buff *skb)
{
mutex_lock(&sock_diag_mutex);
netlink_rcv_skb(skb, &sock_diag_rcv_msg);
mutex_unlock(&sock_diag_mutex);
}
static int sock_diag_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh,
struct netlink_ext_ack *extack)
{
int ret;
switch (nlh->nlmsg_type) {
case TCPDIAG_GETSOCK:
case DCCPDIAG_GETSOCK:
if (inet_rcv_compat == NULL)
sock_load_diag_module(AF_INET, 0);
mutex_lock(&sock_diag_table_mutex);
if (inet_rcv_compat != NULL)
ret = inet_rcv_compat(skb, nlh);
else
ret = -EOPNOTSUPP;
mutex_unlock(&sock_diag_table_mutex);
return ret;
case SOCK_DIAG_BY_FAMILY:
case SOCK_DESTROY:
return __sock_diag_cmd(skb, nlh);
default:
return -EINVAL;
}
}
Because we sendmsg Of msg The type of input parameter is SOCK_DIAG_BY_FAMILY
, So as to go to __sock_diag_cmd Branch
static int __sock_diag_cmd(struct sk_buff *skb, struct nlmsghdr *nlh)
{
int err;
struct sock_diag_req *req = nlmsg_data(nlh);
const struct sock_diag_handler *hndl;
...
mutex_lock(&sock_diag_table_mutex);
hndl = sock_diag_handlers[req->sdiag_family];
if (hndl == NULL)
err = -ENOENT;
else if (nlh->nlmsg_type == SOCK_DIAG_BY_FAMILY)
err = hndl->dump(skb, nlh);
else if (nlh->nlmsg_type == SOCK_DESTROY && hndl->destroy)
err = hndl->destroy(skb, nlh);
else
err = -EOPNOTSUPP;
mutex_unlock(&sock_diag_table_mutex);
return err;
}
Obviously there is another layer ,sock_diag_handlers[req->sdiag_family] For us, it's just , because msg The type is .sdiag_family = AF_INET,
static int __init inet_diag_init(void)
{
const int inet_diag_table_size = (IPPROTO_MAX *
sizeof(struct inet_diag_handler *));
int err = -ENOMEM;
inet_diag_table = kzalloc(inet_diag_table_size, GFP_KERNEL);
if (!inet_diag_table)
goto out;
err = sock_diag_register(&inet_diag_handler);
if (err)
goto out_free_nl;
err = sock_diag_register(&inet6_diag_handler);
if (err)
goto out_free_inet;
static const struct sock_diag_handler inet_diag_handler = {
.family = AF_INET,
.dump = inet_diag_handler_cmd,
.get_info = inet_diag_handler_get_info,
.destroy = inet_diag_handler_cmd,
};
therefore hndl->dump
In fact, what we are talking about is inet_diag_handler_cmd
static int inet_diag_handler_cmd(struct sk_buff *skb, struct nlmsghdr *h)
{
int hdrlen = sizeof(struct inet_diag_req_v2);
struct net *net = sock_net(skb->sk);
if (h->nlmsg_type == SOCK_DIAG_BY_FAMILY &&
h->nlmsg_flags & NLM_F_DUMP) {
....
{
struct netlink_dump_control c = {
.dump = inet_diag_dump,
};
return netlink_dump_start(net->diag_nlsk, skb, h, &c);
}
}
return inet_diag_cmd_exact(h->nlmsg_type, skb, h, nlmsg_data(h));
}
It is conceivable that netlink_dump_start
There must be a call to the input parameter c->dump, namely inet_diag_dump
, It seems so far , Or just rely on the function pointer to call all the way .
inet_diag_dump->__inet_diag_dump->handler->dump
static int __inet_diag_dump(struct sk_buff *skb, struct netlink_callback *cb,
const struct inet_diag_req_v2 *r,
struct nlattr *bc)
{
const struct inet_diag_handler *handler;
int err = 0;
handler = inet_diag_lock_handler(r->sdiag_protocol);
if (!IS_ERR(handler))
handler->dump(skb, cb, r, bc);
else
err = PTR_ERR(handler);
inet_diag_unlock_handler(handler);
return err ? : skb->len;
}
You can see , Then we rely on the registration mechanism , according to sdiag_protocol
Find specific dump A function pointer
static const struct inet_diag_handler tcp_diag_handler = {
.dump = tcp_diag_dump,
.dump_one = tcp_diag_dump_one,
.idiag_get_info = tcp_diag_get_info,
.idiag_get_aux = tcp_diag_get_aux,
.idiag_get_aux_size = tcp_diag_get_aux_size,
.idiag_type = IPPROTO_TCP,
.idiag_info_size = sizeof(struct tcp_info),
#ifdef CONFIG_INET_DIAG_DESTROY
.destroy = tcp_diag_destroy,
#endif
};
static int __init tcp_diag_init(void)
{
return inet_diag_register(&tcp_diag_handler);
}
thus , Finally found tcp_diag Modular dump Function
版权声明
本文为[Mrpre]所创,转载请带上原文链接,感谢
https://yzsam.com/2022/04/202204231409587705.html
边栏推荐
- go基础 反射
- Contraction mapping theorem
- Introduction to Arduino for esp8266 serial port function
- 1n5408-asemi rectifier diode
- 我的 Raspberry Pi Zero 2W 折腾笔记,记录一些遇到的问题和解决办法
- 多语言通信基础 06 go实现grpc的四种数据流模式实现
- Frame synchronization implementation
- OPPO数据湖统一存储技术实践
- The difference between having and where in SQL
- Swift protocol Association object resource name management multithreading GCD delay once
猜你喜欢
Leetcode149 - maximum number of points on a line - Math - hash table
How to design a good API interface?
My raspberry PI zero 2W tossing notes record some problems encountered and solutions
8.5 concise implementation of cyclic neural network
Leetcode151 - invert words in string - String - simulation
QT Detailed explanation of pro file
Mds55-16-asemi rectifier module mds55-16
Nuxt project: Global get process Env information
Reptile exercises (1)
On the day of entry, I cried (mushroom street was laid off and fought for seven months to win the offer)
随机推荐
What is the main purpose of PCIe X1 slot?
Swift protocol Association object resource name management multithreading GCD delay once
Detailed explanation of C language knowledge points -- data types and variables [1] - carry counting system
Select receives both normal data and out of band data
js——实现点击复制功能
Little red book timestamp2 (2022 / 04 / 22)
Alexnet model
Borui data and F5 jointly build the full data chain DNA of financial technology from code to user
SQL中HAVING和WHERE的区别
async void 导致程序崩溃
Openfaas practice 4: template operation
C语言超全学习路线(收藏让你少走弯路)
LeetCode151-颠倒字符串中的单词-字符串-模拟
Epolloneshot event of epoll -- instance program
大文件如何快速上传?
Reptile exercises (1)
LeetCode165-比较版本号-双指针-字符串
JUC学习记录(2022.4.22)
like和regexp差别
go基础 反射