Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add local network scanning feature (ARP) #1557

Merged
merged 23 commits into from
May 20, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
b05ada4
Move dhcp-discover into a dedicated "tools" target
DL6ER May 13, 2023
29a3310
Add pihole-FTL arp-scan [{-v,-a}]
DL6ER May 13, 2023
36bd2a5
Unify warning
DL6ER May 13, 2023
26396bd
Use dedicated counters per MAC for a more accurate per-device reply m…
DL6ER May 14, 2023
65d5b12
Add our own address to the scan results so we can detect IP conflicts…
DL6ER May 14, 2023
0e74485
Include hostnames (if available)
DL6ER May 14, 2023
041092a
Print progress in verbose arp-scanning mode
DL6ER May 15, 2023
1e0f20b
Print different warnings if we received multiple replies from (appare…
DL6ER May 15, 2023
f1b4a33
Consolidate output in main process
DL6ER May 15, 2023
5dcdbd7
Scale progress percentage according to number of addresses to be scan…
DL6ER May 16, 2023
1bc05b9
Only print progress if it has changed. Otherwise, print "." as hearth…
DL6ER May 16, 2023
4387ff2
Always skip the loopback interface, also in "-a" mode
DL6ER May 16, 2023
d45a7c4
Interface names can be up to 16 bytes long. Docker bridge interfaces …
DL6ER May 16, 2023
2d6a361
Clearly log when scanning interfaces failed
DL6ER May 16, 2023
8a29911
Log more verbose human-readable error string if available
DL6ER May 16, 2023
e3550c1
Add capabilities check for CAP_NET_RAW (root always has it)
DL6ER May 16, 2023
73e3d88
Optimize thread_data structure and store a thread-local copy of the i…
DL6ER May 17, 2023
6ce6eae
Add arp-scan -xtreme mode for very unreliable connections
DL6ER May 17, 2023
ba8807c
Give reply rate in percent instead of showing the reply matrix
DL6ER May 17, 2023
615be9c
Exit early if insufficient memory is available, perform as many inter…
DL6ER May 18, 2023
4d7640c
Reduce memory requirements by factor 4x
DL6ER May 18, 2023
d1f70d7
Further reduce memory requirements by factor 10x (if not in -x mode)
DL6ER May 18, 2023
31a90da
Align % in reply rate column
DL6ER May 20, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions src/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -116,8 +116,6 @@ set(sources
daemon.h
datastructure.c
datastructure.h
dhcp-discover.c
dhcp-discover.h
dnsmasq_interface.c
dnsmasq_interface.h
edns0.c
Expand Down Expand Up @@ -180,6 +178,7 @@ add_executable(pihole-FTL
$<TARGET_OBJECTS:lua>
$<TARGET_OBJECTS:tre-regex>
$<TARGET_OBJECTS:syscalls>
$<TARGET_OBJECTS:tools>
)
if(STATIC STREQUAL "true")
set_target_properties(pihole-FTL PROPERTIES LINK_SEARCH_START_STATIC ON)
Expand Down Expand Up @@ -253,3 +252,4 @@ add_subdirectory(lua)
add_subdirectory(lua/scripts)
add_subdirectory(tre-regex)
add_subdirectory(syscalls)
add_subdirectory(tools)
40 changes: 29 additions & 11 deletions src/args.c
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,9 @@
// LUA dependencies
#include "lua/ftl_lua.h"
// run_dhcp_discover()
#include "dhcp-discover.h"
#include "tools/dhcp-discover.h"
// run_arp_scan()
#include "tools/arp-scan.h"
// defined in dnsmasq.c
extern void print_dnsmasq_version(const char *yellow, const char *green, const char *bold, const char *normal);

Expand Down Expand Up @@ -163,6 +165,24 @@ void parse_args(int argc, char* argv[])
(argc > 1 && strEndsWith(argv[1], ".db")))
exit(sqlite3_shell_main(argc, argv));

// DHCP discovery mode
if(argc > 1 && strcmp(argv[1], "dhcp-discover") == 0)
{
// Enable stdout printing
cli_mode = true;
exit(run_dhcp_discover());
}

// ARP scanning mode
if(argc > 1 && strcmp(argv[1], "arp-scan") == 0)
{
// Enable stdout printing
cli_mode = true;
const bool scan_all = argc > 2 && strcmp(argv[2], "-a") == 0;
const bool extreme_mode = argc > 2 && strcmp(argv[2], "-x") == 0;
exit(run_arp_scan(scan_all, extreme_mode));
}

// start from 1, as argv[0] is the executable name
for(int i = 1; i < argc; i++)
{
Expand Down Expand Up @@ -415,14 +435,6 @@ void parse_args(int argc, char* argv[])
}
}

// Regex test mode
if(strcmp(argv[i], "dhcp-discover") == 0)
{
// Enable stdout printing
cli_mode = true;
exit(run_dhcp_discover());
}

// List of implemented arguments
if(strcmp(argv[i], "-h") == 0 || strcmp(argv[i], "help") == 0 || strcmp(argv[i], "--help") == 0)
{
Expand Down Expand Up @@ -495,13 +507,19 @@ void parse_args(int argc, char* argv[])

printf("%sDebugging and special use:%s\n", yellow, normal);
printf("\t%sd%s, %sdebug%s Enter debugging mode\n", green, normal, green, normal);
printf("\t%stest%s Don't start pihole-FTL but\n", green, normal);
printf("\t instead quit immediately\n");
printf("\t%stest%s Don't start pihole-FTL but instead\n", green, normal);
printf("\t quit immediately\n");
printf("\t%s-f%s, %sno-daemon%s Don't go into daemon mode\n\n", green, normal, green, normal);

printf("%sOther:%s\n", yellow, normal);
printf("\t%sdhcp-discover%s Discover DHCP servers in the local\n", green, normal);
printf("\t network\n");
printf("\t%sarp-scan %s[-a/-x]%s Use ARP to scan local network for\n", green, cyan, normal);
printf("\t possible IP conflicts\n");
printf("\t Append %s-a%s to force scan on all\n", cyan, normal);
printf("\t interfaces\n");
printf("\t Append %s-x%s to force scan on all\n", cyan, normal);
printf("\t interfaces and scan 10x more often\n");
printf("\t%s-h%s, %shelp%s Display this help and exit\n\n", green, normal, green, normal);
exit(EXIT_SUCCESS);
}
Expand Down
41 changes: 41 additions & 0 deletions src/capabilities.c
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,47 @@ static const unsigned int capabilityIDs[] = { CAP_CHOWN , CAP_DAC_OVERRIDE ,
static const char* capabilityNames[] = {"CAP_CHOWN", "CAP_DAC_OVERRIDE", "CAP_DAC_READ_SEARCH", "CAP_FOWNER", "CAP_FSETID", "CAP_KILL", "CAP_SETGID", "CAP_SETUID", "CAP_SETPCAP", "CAP_LINUX_IMMUTABLE", "CAP_NET_BIND_SERVICE", "CAP_NET_BROADCAST", "CAP_NET_ADMIN", "CAP_NET_RAW", "CAP_IPC_LOCK", "CAP_IPC_OWNER", "CAP_SYS_MODULE", "CAP_SYS_RAWIO", "CAP_SYS_CHROOT", "CAP_SYS_PTRACE", "CAP_SYS_PACCT", "CAP_SYS_ADMIN", "CAP_SYS_BOOT", "CAP_SYS_NICE", "CAP_SYS_RESOURCE", "CAP_SYS_TIME", "CAP_SYS_TTY_CONFIG", "CAP_MKNOD", "CAP_LEASE", "CAP_AUDIT_WRITE", "CAP_AUDIT_CONTROL", "CAP_SETFCAP"};
static const unsigned int numCaps = sizeof(capabilityIDs) / sizeof(*capabilityIDs);

bool check_capability(const unsigned int cap)
{
// First assume header version 1
int capsize = 1; // VFS_CAP_U32_1
cap_user_data_t data = NULL;
cap_user_header_t hdr = calloc(sizeof(*hdr), capsize);

// Determine capabilities version used by the current kernel
capget(hdr, NULL);

// Check version
if (hdr->version != LINUX_CAPABILITY_VERSION_1)
{
// If unknown version, use largest supported version (3)
// Version 2 is deprecated according to linux/capability.h
if (hdr->version != LINUX_CAPABILITY_VERSION_2)
{
hdr->version = LINUX_CAPABILITY_VERSION_3;
capsize = 2; // VFS_CAP_U32_3
}
else
{
// Use version 2
capsize = 2; // VFS_CAP_U32_2
}
}

// Get current capabilities
data = calloc(sizeof(*data), capsize);
capget(hdr, data);

// Check if the capability is available
const bool available = ((data->permitted & (1 << cap)) && (data->effective & (1 << cap)));

// Free memory
free(hdr);
free(data);

return available;
}

bool check_capabilities(void)
{
// First assume header version 1
Expand Down
1 change: 1 addition & 0 deletions src/capabilities.h
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
#ifndef CAPABILITIES_H
#define CAPABILITIES_H

bool check_capability(const unsigned int cap);
bool check_capabilities(void);

#endif //CAPABILITIES_H
11 changes: 7 additions & 4 deletions src/syscalls/recvfrom.c
Original file line number Diff line number Diff line change
Expand Up @@ -32,11 +32,14 @@ ssize_t FTLrecvfrom(int sockfd, void *buf, size_t len, int flags, struct sockadd
// Backup errno value
const int _errno = errno;

// Final error checking (may have failed for some other reason then an
// EINTR = interrupted system call)
if(ret < 0)
// Final error checking. May have failed for some other reason then an
// EINTR = interrupted system call. In that case, log a warning However,
// if the error is EAGAIN, this is not an error, but just a non-blocking
// socket that has no data available or we ran into an (expected)
// timeout. In that case, do not log a warning
if(ret < 0 && errno != EAGAIN)
logg("WARN: Could not recvfrom() in %s() (%s:%i): %s",
func, file, line, strerror(errno));
func, file, line, strerror(errno));

// Restore errno value
errno = _errno;
Expand Down
4 changes: 2 additions & 2 deletions src/syscalls/sendto.c
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,8 @@ ssize_t FTLsendto(int sockfd, void *buf, size_t len, int flags, const struct soc
const int _errno = errno;

// Final error checking (may have failed for some other reason then an
// EINTR = interrupted system call)
if(ret < 0)
// EINTR = interrupted system call), also ignore EPROTONOSUPPORT (ARP scanning)
if(ret < 0 && errno != EPROTONOSUPPORT)
logg("WARN: Could not sendto() in %s() (%s:%i): %s",
func, file, line, strerror(errno));

Expand Down
20 changes: 20 additions & 0 deletions src/tools/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# Pi-hole: A black hole for Internet advertisements
# (c) 2020 Pi-hole, LLC (https://pi-hole.net)
# Network-wide ad blocking via your own hardware.
#
# FTL Engine
# /src/tools/CMakeList.txt
#
# This file is copyright under the latest version of the EUPL.
# Please see LICENSE file for your rights under this license.

set(tools_sources
arp-scan.c
arp-scan.h
dhcp-discover.c
dhcp-discover.h
)

add_library(tools OBJECT ${tools_sources})
target_compile_options(tools PRIVATE "${EXTRAWARN}")
target_include_directories(tools PRIVATE ${PROJECT_SOURCE_DIR}/src)
Loading