Skip to content
API Documentation
BastionGuard™
bsc::net::anonymous_namespace{SockDiag.cpp} Namespace Reference

Functions

std::optional< SockInfo > sockdiag_query (int family, int protocol, const std::string &src_ip, std::uint16_t src_port, const std::string &dst_ip, std::uint16_t dst_port)

Function Documentation

◆ sockdiag_query()

std::optional< SockInfo > bsc::net::anonymous_namespace{SockDiag.cpp}::sockdiag_query ( int family,
int protocol,
const std::string & src_ip,
std::uint16_t src_port,
const std::string & dst_ip,
std::uint16_t dst_port )

Definition at line 25 of file SockDiag.cpp.

28{
29 int fd = ::socket(AF_NETLINK, SOCK_DGRAM | SOCK_CLOEXEC, NETLINK_SOCK_DIAG);
30 if (fd < 0) return std::nullopt;
31
32 // Timeout 100ms sulla ricezione: non dobbiamo mai bloccare NFQUEUE
33 // per tempi lunghi. Il kernel risponde in microsecondi quando trova.
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 // Prepara la richiesta inet_diag con filtro esatto sulla 4-tupla.
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 // Stati TCP di interesse: tutti per UDP (che non ha stati).
53 // Per TCP accettiamo anche SYN_SENT (3) perche' NFQUEUE ci consegna il
54 // SYN prima che la connessione sia stabilita. Bitmask completa per
55 // coprire tutti i casi utili.
56 msg.req.idiag_states = 0xFFFFFFFFu;
57 msg.req.idiag_ext = 0;
58
59 // Popola la 4-tupla nel filtro. Attenzione: saddr/daddr sono in
60 // network-byte-order; porte idem.
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
67 if (family == AF_INET) {
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 // Invia al kernel (dest: kernel = pid 0)
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 // Ricevi la risposta
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 // NLMSG_NEXT decrementa in-place il contatore, quindi n non puo'
98 // essere const.
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}
#define AF_INET
Definition bsc_ebpf.c:56
#define AF_INET6
Definition bsc_ebpf.c:57

References AF_INET, AF_INET6, bsc::net::SockInfo::inode, sockdiag_query(), and bsc::net::SockInfo::uid.

Referenced by sockdiag_query().