28{
29 int fd = ::socket(AF_NETLINK, SOCK_DGRAM | SOCK_CLOEXEC, NETLINK_SOCK_DIAG);
30 if (fd < 0) return std::nullopt;
31
32
33
34 struct timeval tv{};
35 tv.tv_sec = 0;
36 tv.tv_usec = 100 * 1000;
37 ::setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv));
38
39
40 struct {
41 struct nlmsghdr nlh;
42 struct inet_diag_req_v2 req;
43 } msg{};
44
45 msg.nlh.nlmsg_len = NLMSG_LENGTH(sizeof(msg.req));
46 msg.nlh.nlmsg_type = SOCK_DIAG_BY_FAMILY;
47 msg.nlh.nlmsg_flags = NLM_F_REQUEST;
48 msg.nlh.nlmsg_seq = 1;
49
50 msg.req.sdiag_family = static_cast<std::uint8_t>(family);
51 msg.req.sdiag_protocol = static_cast<std::uint8_t>(protocol);
52
53
54
55
56 msg.req.idiag_states = 0xFFFFFFFFu;
57 msg.req.idiag_ext = 0;
58
59
60
61 msg.req.id.idiag_sport = htons(src_port);
62 msg.req.id.idiag_dport = htons(dst_port);
63 msg.req.id.idiag_if = 0;
64 msg.req.id.idiag_cookie[0] = INET_DIAG_NOCOOKIE;
65 msg.req.id.idiag_cookie[1] = INET_DIAG_NOCOOKIE;
66
68 in_addr sa{}, da{};
69 if (inet_pton(
AF_INET, src_ip.c_str(), &sa) != 1 ||
70 inet_pton(
AF_INET, dst_ip.c_str(), &da) != 1) {
71 ::close(fd); return std::nullopt;
72 }
73 std::memcpy(msg.req.id.idiag_src, &sa.s_addr, 4);
74 std::memcpy(msg.req.id.idiag_dst, &da.s_addr, 4);
75 } else {
76 in6_addr sa{}, da{};
77 if (inet_pton(
AF_INET6, src_ip.c_str(), &sa) != 1 ||
78 inet_pton(
AF_INET6, dst_ip.c_str(), &da) != 1) {
79 ::close(fd); return std::nullopt;
80 }
81 std::memcpy(msg.req.id.idiag_src, &sa.s6_addr, 16);
82 std::memcpy(msg.req.id.idiag_dst, &da.s6_addr, 16);
83 }
84
85
86 struct sockaddr_nl dest{};
87 dest.nl_family = AF_NETLINK;
88 const auto sent = ::sendto(fd, &msg, msg.nlh.nlmsg_len, 0,
89 reinterpret_cast<struct sockaddr*>(&dest), sizeof(dest));
90 if (sent < 0) { ::close(fd); return std::nullopt; }
91
92
93 char buf[8192];
94 const auto rcv = ::recv(fd, buf, sizeof(buf), 0);
95 ::close(fd);
96 if (rcv <= 0) return std::nullopt;
97
98
99 auto n = static_cast<unsigned int>(rcv);
100
101 struct nlmsghdr* nh = reinterpret_cast<struct nlmsghdr*>(buf);
102 for (; NLMSG_OK(nh, n); nh = NLMSG_NEXT(nh, n)) {
103 if (nh->nlmsg_type == NLMSG_DONE) break;
104 if (nh->nlmsg_type == NLMSG_ERROR) return std::nullopt;
105 if (nh->nlmsg_type != SOCK_DIAG_BY_FAMILY) continue;
106
107 auto* diag = reinterpret_cast<struct inet_diag_msg*>(NLMSG_DATA(nh));
108 SockInfo info;
109 info.inode = diag->idiag_inode;
110 info.uid = diag->idiag_uid;
111 if (info.inode != 0) return info;
112 }
113 return std::nullopt;
114}