Skip to content

Commit 7f5ecfb

Browse files
committed
refactor(userspace/libscap): use common helper for sock table parsing
Signed-off-by: Leonardo Di Giovanna <leonardodigiovanna1@gmail.com>
1 parent 4bfe93d commit 7f5ecfb

File tree

1 file changed

+83
-151
lines changed

1 file changed

+83
-151
lines changed

userspace/libscap/linux/scap_fds.c

Lines changed: 83 additions & 151 deletions
Original file line numberDiff line numberDiff line change
@@ -921,118 +921,6 @@ static int32_t parse_ipv4_socket_table_line(const char *const line_start,
921921
return SCAP_SUCCESS;
922922
}
923923

924-
static int32_t parse_procfs_proc_pid_net_ipv4_file_impl(const int fd,
925-
const char *const filename,
926-
scap_fdinfo **sockets,
927-
const int l4proto,
928-
char *const error) {
929-
// note: 32 kB is a good choice for the majority of the use cases. Each file line is
930-
// approximately 150 bytes. The following table estimate how many read() system call are issued
931-
// in the optimistic case (e.g.: no signals):
932-
// - 100 sockets -> ~15 kB -> 1 read()
933-
// - 1000 sockets -> ~150 kB -> ~5 read()
934-
// - 10000 sockets -> ~1.5 MB -> ~50 read()
935-
// Even in the worst scenario, the cost of issuing 50 system call should be overcome by the
936-
// cache-friendly accesses using the stack-allocated buffer.
937-
char buff[32 * 1024];
938-
// `bytes_in_buff` accounts for the total amount of data currently present in `buff`.
939-
size_t bytes_in_buff = 0;
940-
while(1) {
941-
const ssize_t read_bytes = read(fd, buff + bytes_in_buff, sizeof(buff) - bytes_in_buff);
942-
if(read_bytes < 0) {
943-
if(errno == EINTR) { // Re-attempt upon signal.
944-
continue;
945-
}
946-
return scap_errprintf(error, errno, "can't read socket table file %s", filename);
947-
}
948-
if(read_bytes == 0) {
949-
return SCAP_SUCCESS;
950-
}
951-
bytes_in_buff += read_bytes;
952-
953-
// Calculate the buffer valid data range, consisting of all read data up to the last '\n'
954-
// (i.e. excluding the trailing truncated new line, if present).
955-
// note: if we cannot find any '\n', we set `buff_valid_len` to 0 and see later if we can
956-
// recover a complete line with a shift logic and a subsequent read.
957-
// optimization: search for '\n' only in the new read data, because we are sure any old data
958-
// doesn't contain it.
959-
const char *const new_data_start = buff + bytes_in_buff - read_bytes;
960-
const char *const last_newline = memrchr(new_data_start, '\n', read_bytes);
961-
const size_t buff_valid_len = last_newline ? last_newline - buff + 1 : 0;
962-
963-
const char *line_start = buff;
964-
const char *buff_valid_end = buff + buff_valid_len;
965-
// note: if `buff_valid_len` is 0, this loop doesn't run.
966-
while(line_start < buff_valid_end) {
967-
char *const line_end = memchr(line_start, '\n', buff_valid_end - line_start);
968-
if(!line_end) {
969-
// bug: if we enter the loop, the range [line_start, buff_valid_end] contains '\n',
970-
// so it's impossible to end up here.
971-
ASSERT(false);
972-
return scap_errprintf(error,
973-
0,
974-
"bug found while parsing socket table file %s: unexpected "
975-
"line with no newline",
976-
filename);
977-
}
978-
// Replace '\n' with '\0' to make string-related API work.
979-
// note: the original '\n' is restored at the end of the iteration.
980-
*line_end = '\0';
981-
982-
const int32_t res =
983-
parse_ipv4_socket_table_line(line_start, line_end, sockets, l4proto, error);
984-
if(res != SCAP_SUCCESS) {
985-
return res;
986-
}
987-
988-
*line_end = '\n';
989-
line_start = line_end + 1;
990-
}
991-
992-
// Apply shifting logic to move the truncated trailing line (if any) at the beginning of the
993-
// buffer.
994-
// note: this remove from the buffer any processed data, that is data in the range
995-
// [buff, buff+buff_valid_len]).
996-
// note: the shift is not applied if we haven't processed any data in this iteration.
997-
const size_t buff_unprocessed_data_len = bytes_in_buff - buff_valid_len;
998-
if(buff_unprocessed_data_len > 0 && buff_valid_len > 0) {
999-
memmove(buff, buff + buff_valid_len, buff_unprocessed_data_len);
1000-
}
1001-
// Now the buffer contains only unprocessed data.
1002-
bytes_in_buff = buff_unprocessed_data_len;
1003-
if(bytes_in_buff == sizeof(buff)) {
1004-
// It is almost impossible we filled the entire buffer with something not containing any
1005-
// '\n' character. We don't have much to do here: just returning.
1006-
ASSERT(false);
1007-
return SCAP_SUCCESS;
1008-
}
1009-
}
1010-
1011-
// This is unreachable!
1012-
ASSERT(false);
1013-
return scap_errprintf(error,
1014-
0,
1015-
"bug found while parsing socket table file %s: control should never "
1016-
"reach any statement after the outer while loop in "
1017-
"parse_procfs_proc_pid_net_ipv4_file_impl()!",
1018-
filename);
1019-
}
1020-
1021-
static int32_t parse_procfs_proc_pid_net_ipv4_file(const char *filename,
1022-
const int l4proto,
1023-
scap_fdinfo **sockets,
1024-
char *const error) {
1025-
const int fd = open(filename, O_RDONLY, 0);
1026-
if(fd == -1) {
1027-
return scap_errprintf(error, errno, "can't open socket table file %s", filename);
1028-
}
1029-
1030-
const int32_t res =
1031-
parse_procfs_proc_pid_net_ipv4_file_impl(fd, filename, sockets, l4proto, error);
1032-
close(fd);
1033-
return res;
1034-
}
1035-
1036924
// Convert a single hex char to 0-15. `c` must be a valid hex char (i.e.: '0'-'9','a'-'f','A'-'F').
1037925
static uint32_t hex_char_to_u32(const char c) {
1038926
return (c & 0xF) + 9 * (c >> 6);
@@ -1163,11 +1051,12 @@ static int32_t parse_ipv6_socket_table_line(const char *const line_start,
11631051
return SCAP_SUCCESS;
11641052
}
11651053

1166-
static int32_t parse_procfs_proc_pid_net_ipv6_file_impl(const int fd,
1167-
const char *const filename,
1168-
scap_fdinfo **sockets,
1169-
const int l4proto,
1170-
char *const error) {
1054+
static int32_t parse_procfs_proc_pid_socket_table_file_impl(const int fd,
1055+
const char *const filename,
1056+
const int socket_domain,
1057+
scap_fdinfo **sockets,
1058+
const int l4proto,
1059+
char *const error) {
11711060
// note: 32 kB is a good choice for the majority of the use cases. Each file line is
11721061
// approximately 150 bytes. The following table estimate how many read() system call are issued
11731062
// in the optimistic case (e.g.: no signals):
@@ -1221,8 +1110,26 @@ static int32_t parse_procfs_proc_pid_net_ipv6_file_impl(const int fd,
12211110
// note: the original '\n' is restored at the end of the iteration.
12221111
*line_end = '\0';
12231112

1224-
const int32_t res =
1225-
parse_ipv6_socket_table_line(line_start, line_end, sockets, l4proto, error);
1113+
int32_t res = SCAP_FAILURE;
1114+
switch(socket_domain) {
1115+
case AF_INET: {
1116+
res = parse_ipv4_socket_table_line(line_start, line_end, sockets, l4proto, error);
1117+
break;
1118+
}
1119+
case AF_INET6: {
1120+
res = parse_ipv6_socket_table_line(line_start, line_end, sockets, l4proto, error);
1121+
break;
1122+
}
1123+
default: {
1124+
ASSERT(false);
1125+
return scap_errprintf(
1126+
error,
1127+
0,
1128+
"bug found while parsing socket table file %s: unknown socket domain %d",
1129+
filename,
1130+
socket_domain);
1131+
}
1132+
}
12261133
if(res != SCAP_SUCCESS) {
12271134
return res;
12281135
}
@@ -1256,21 +1163,26 @@ static int32_t parse_procfs_proc_pid_net_ipv6_file_impl(const int fd,
12561163
0,
12571164
"bug found while parsing socket table file %s: control should never "
12581165
"reach any statement after the outer while loop in "
1259-
"parse_procfs_proc_pid_net_ipv6_file_impl()!",
1166+
"parse_procfs_proc_pid_socket_table_file_impl()!",
12601167
filename);
12611168
}
12621169

1263-
static int32_t parse_procfs_proc_pid_net_ipv6_file(const char *filename,
1264-
const int l4proto,
1265-
scap_fdinfo **sockets,
1266-
char *const error) {
1170+
static int32_t parse_procfs_proc_pid_socket_table_file(const char *filename,
1171+
const int socket_domain,
1172+
const int l4proto,
1173+
scap_fdinfo **sockets,
1174+
char *const error) {
12671175
const int fd = open(filename, O_RDONLY, 0);
12681176
if(fd == -1) {
12691177
return scap_errprintf(error, errno, "can't open socket table file %s", filename);
12701178
}
12711179

1272-
const int32_t res =
1273-
parse_procfs_proc_pid_net_ipv6_file_impl(fd, filename, sockets, l4proto, error);
1180+
const int32_t res = parse_procfs_proc_pid_socket_table_file_impl(fd,
1181+
filename,
1182+
socket_domain,
1183+
sockets,
1184+
l4proto,
1185+
error);
12741186
close(fd);
12751187
return res;
12761188
}
@@ -1293,22 +1205,31 @@ int32_t scap_fd_read_sockets(char *procdir, struct scap_ns_socket_list *sockets,
12931205
}
12941206

12951207
snprintf(filename, sizeof(filename), "%stcp", netroot);
1296-
if(parse_procfs_proc_pid_net_ipv4_file(filename, SCAP_L4_TCP, &sockets->sockets, err_buf) ==
1297-
SCAP_FAILURE) {
1208+
if(parse_procfs_proc_pid_socket_table_file(filename,
1209+
AF_INET,
1210+
SCAP_L4_TCP,
1211+
&sockets->sockets,
1212+
err_buf) == SCAP_FAILURE) {
12981213
scap_fd_free_table(&sockets->sockets);
12991214
return scap_errprintf(error, 0, "Could not read ipv4 tcp sockets (%s)", err_buf);
13001215
}
13011216

13021217
snprintf(filename, sizeof(filename), "%sudp", netroot);
1303-
if(parse_procfs_proc_pid_net_ipv4_file(filename, SCAP_L4_UDP, &sockets->sockets, err_buf) ==
1304-
SCAP_FAILURE) {
1218+
if(parse_procfs_proc_pid_socket_table_file(filename,
1219+
AF_INET,
1220+
SCAP_L4_UDP,
1221+
&sockets->sockets,
1222+
err_buf) == SCAP_FAILURE) {
13051223
scap_fd_free_table(&sockets->sockets);
13061224
return scap_errprintf(error, 0, "Could not read ipv4 udp sockets (%s)", err_buf);
13071225
}
13081226

13091227
snprintf(filename, sizeof(filename), "%sraw", netroot);
1310-
if(parse_procfs_proc_pid_net_ipv4_file(filename, SCAP_L4_RAW, &sockets->sockets, err_buf) ==
1311-
SCAP_FAILURE) {
1228+
if(parse_procfs_proc_pid_socket_table_file(filename,
1229+
AF_INET,
1230+
SCAP_L4_RAW,
1231+
&sockets->sockets,
1232+
err_buf) == SCAP_FAILURE) {
13121233
scap_fd_free_table(&sockets->sockets);
13131234
return scap_errprintf(error, 0, "Could not read ipv4 raw sockets (%s)", err_buf);
13141235
}
@@ -1328,27 +1249,38 @@ int32_t scap_fd_read_sockets(char *procdir, struct scap_ns_socket_list *sockets,
13281249
}
13291250

13301251
snprintf(filename, sizeof(filename), "%stcp6", netroot);
1331-
/* We assume if there is /proc/net/tcp6 that ipv6 is available */
1332-
if(access(filename, R_OK) == 0) {
1333-
if(parse_procfs_proc_pid_net_ipv6_file(filename, SCAP_L4_TCP, &sockets->sockets, err_buf) ==
1334-
SCAP_FAILURE) {
1335-
scap_fd_free_table(&sockets->sockets);
1336-
return scap_errprintf(error, 0, "Could not read ipv6 tcp sockets (%s)", err_buf);
1337-
}
1252+
// We assume IPv6 isn't available if /proc/net/tcp6 is not available.
1253+
if(access(filename, R_OK) != 0) {
1254+
return SCAP_SUCCESS;
1255+
}
13381256

1339-
snprintf(filename, sizeof(filename), "%sudp6", netroot);
1340-
if(parse_procfs_proc_pid_net_ipv6_file(filename, SCAP_L4_UDP, &sockets->sockets, err_buf) ==
1341-
SCAP_FAILURE) {
1342-
scap_fd_free_table(&sockets->sockets);
1343-
return scap_errprintf(error, 0, "Could not read ipv6 udp sockets (%s)", err_buf);
1344-
}
1257+
if(parse_procfs_proc_pid_socket_table_file(filename,
1258+
AF_INET6,
1259+
SCAP_L4_TCP,
1260+
&sockets->sockets,
1261+
err_buf) == SCAP_FAILURE) {
1262+
scap_fd_free_table(&sockets->sockets);
1263+
return scap_errprintf(error, 0, "Could not read ipv6 tcp sockets (%s)", err_buf);
1264+
}
13451265

1346-
snprintf(filename, sizeof(filename), "%sraw6", netroot);
1347-
if(parse_procfs_proc_pid_net_ipv6_file(filename, SCAP_L4_RAW, &sockets->sockets, err_buf) ==
1348-
SCAP_FAILURE) {
1349-
scap_fd_free_table(&sockets->sockets);
1350-
return scap_errprintf(error, 0, "Could not read ipv6 raw sockets (%s)", err_buf);
1351-
}
1266+
snprintf(filename, sizeof(filename), "%sudp6", netroot);
1267+
if(parse_procfs_proc_pid_socket_table_file(filename,
1268+
AF_INET6,
1269+
SCAP_L4_UDP,
1270+
&sockets->sockets,
1271+
err_buf) == SCAP_FAILURE) {
1272+
scap_fd_free_table(&sockets->sockets);
1273+
return scap_errprintf(error, 0, "Could not read ipv6 udp sockets (%s)", err_buf);
1274+
}
1275+
1276+
snprintf(filename, sizeof(filename), "%sraw6", netroot);
1277+
if(parse_procfs_proc_pid_socket_table_file(filename,
1278+
AF_INET6,
1279+
SCAP_L4_RAW,
1280+
&sockets->sockets,
1281+
err_buf) == SCAP_FAILURE) {
1282+
scap_fd_free_table(&sockets->sockets);
1283+
return scap_errprintf(error, 0, "Could not read ipv6 raw sockets (%s)", err_buf);
13521284
}
13531285

13541286
return SCAP_SUCCESS;

0 commit comments

Comments
 (0)