feat: initial build
This commit is contained in:
commit
ac436fae7e
5 changed files with 210 additions and 0 deletions
9
.dockerignore
Normal file
9
.dockerignore
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
.git
|
||||
.gitignore
|
||||
build
|
||||
cmake-build-*
|
||||
.vscode
|
||||
.idea
|
||||
*.o
|
||||
*.obj
|
||||
*.log
|
||||
15
CMakeLists.txt
Normal file
15
CMakeLists.txt
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
cmake_minimum_required(VERSION 3.10)
|
||||
project(ipv6)
|
||||
|
||||
# Fügen Sie die folgenden Zeilen hinzu, um cURL zu Ihrem Projekt hinzuzufügen
|
||||
find_package(CURL REQUIRED)
|
||||
include_directories(${CURL_INCLUDE_DIRS})
|
||||
|
||||
# Setzen Sie das Ausgabeverzeichnis für ausführbare Dateien
|
||||
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin)
|
||||
|
||||
# Fügen Sie Ihre C++-Quelldateien hinzu
|
||||
add_executable(ipv6 src/main.cpp)
|
||||
|
||||
# Linken Sie Ihr ausführbares Programm mit der cURL-Bibliothek
|
||||
target_link_libraries(ipv6 ${CURL_LIBRARIES})
|
||||
41
Dockerfile
Normal file
41
Dockerfile
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
# ---- Build stage ------------------------------------------------------------
|
||||
FROM debian:bookworm-slim AS build
|
||||
|
||||
RUN apt-get update && apt-get install -y --no-install-recommends \
|
||||
build-essential cmake pkg-config \
|
||||
libcurl4-openssl-dev ca-certificates \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
WORKDIR /app
|
||||
COPY CMakeLists.txt .
|
||||
COPY src ./src
|
||||
|
||||
# Build
|
||||
RUN mkdir -p build && cd build && cmake .. -DCMAKE_BUILD_TYPE=Release && cmake --build . --config Release
|
||||
|
||||
# ---- Runtime stage ----------------------------------------------------------
|
||||
FROM debian:bookworm-slim
|
||||
|
||||
# nur Runtime-Libs
|
||||
RUN apt-get update && apt-get install -y --no-install-recommends \
|
||||
libcurl4 ca-certificates \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
# non-root User
|
||||
RUN useradd -r -u 10001 appuser
|
||||
|
||||
WORKDIR /app
|
||||
COPY --from=build /app/build/ipv6_logger /app/ipv6_logger
|
||||
|
||||
# Standard-Env (kannst du per Compose/CLI überschreiben)
|
||||
ENV URL="http://j-massing.de:19121/logs" \
|
||||
INTERVAL="60"
|
||||
|
||||
# Healthcheck: prüft IPv6-Erreichbarkeit von 1.1.1.1 (via curl connect-only)
|
||||
HEALTHCHECK --interval=60s --timeout=5s --start-period=10s --retries=3 \
|
||||
CMD curl --connect-timeout 3 --silent --head http://1.1.1.1 || exit 1
|
||||
|
||||
USER appuser
|
||||
|
||||
# Loggt Hostname beim Start und läuft als Daemon-Loop
|
||||
ENTRYPOINT ["/app/ipv6_logger"]
|
||||
13
docker-compose.yaml
Normal file
13
docker-compose.yaml
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
version: "3.8"
|
||||
services:
|
||||
ipv6-client:
|
||||
build: .
|
||||
image: ipv6-client:latest
|
||||
container_name: ipv6-client
|
||||
network_mode: host # <<< sieht Host-Interfaces u. echte IPv6
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
HOSTNAME: "${HOSTNAME:jenspi}"
|
||||
URL: "http://j-massing.de:19121/logs"
|
||||
INTERVAL: "60" # Seconds
|
||||
# Keine Ports nötig, da client-only
|
||||
132
src/main.cpp
Normal file
132
src/main.cpp
Normal file
|
|
@ -0,0 +1,132 @@
|
|||
#include <iostream>
|
||||
#include <string>
|
||||
#include <cstring>
|
||||
#include <ifaddrs.h>
|
||||
#include <netinet/in.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <net/if.h>
|
||||
#include <curl/curl.h>
|
||||
#include <thread>
|
||||
#include <chrono>
|
||||
#include <fstream>
|
||||
#include <vector>
|
||||
#include <filesystem>
|
||||
#include <algorithm>
|
||||
|
||||
bool isPhysicalInterface(const char *interfaceName)
|
||||
{
|
||||
return (std::strncmp(interfaceName, "eth", 3) == 0 || std::strncmp(interfaceName, "wlan", 4) == 0);
|
||||
}
|
||||
|
||||
std::string getGlobalIpv6()
|
||||
{
|
||||
std::string ipv6;
|
||||
struct ifaddrs *handle, *addressInfo;
|
||||
if (getifaddrs(&handle) == -1)
|
||||
{
|
||||
std::cerr << "Error getting interface addresses" << std::endl;
|
||||
return "";
|
||||
}
|
||||
|
||||
for (addressInfo = handle; addressInfo != nullptr; addressInfo = addressInfo->ifa_next)
|
||||
{
|
||||
if (addressInfo->ifa_addr == nullptr || addressInfo->ifa_addr->sa_family != AF_INET6 || !isPhysicalInterface(addressInfo->ifa_name))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
char addr[INET6_ADDRSTRLEN];
|
||||
if (inet_ntop(AF_INET6, &((struct sockaddr_in6 *)addressInfo->ifa_addr)->sin6_addr, addr, sizeof(addr)) == nullptr)
|
||||
{
|
||||
perror("inet_ntop");
|
||||
continue;
|
||||
}
|
||||
|
||||
/* INFO: Check if is GUA (Global unique address) currently only reserved are 2xxx and 3xxx */
|
||||
if (addr[0] != '2' && addr[0] != '3')
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if ((addressInfo->ifa_flags & IFF_UP) && !(addressInfo->ifa_flags & (IFF_LOOPBACK | IFF_POINTOPOINT)) && !IN6_IS_ADDR_LINKLOCAL(&((struct sockaddr_in6 *)addressInfo->ifa_addr)->sin6_addr))
|
||||
{
|
||||
ipv6 = addr;
|
||||
break;
|
||||
}
|
||||
}
|
||||
freeifaddrs(handle);
|
||||
return ipv6;
|
||||
}
|
||||
|
||||
void send(const std::string &url, const std::string &data)
|
||||
{
|
||||
CURL *curl = curl_easy_init();
|
||||
if (!curl)
|
||||
{
|
||||
std::cerr << "Failed to initialize CURL" << std::endl;
|
||||
return;
|
||||
}
|
||||
|
||||
struct curl_slist *headers = nullptr;
|
||||
headers = curl_slist_append(headers, "Content-Type: text/plain");
|
||||
|
||||
curl_easy_setopt(curl, CURLOPT_URL, url.c_str());
|
||||
curl_easy_setopt(curl, CURLOPT_POST, 1L);
|
||||
curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);
|
||||
curl_easy_setopt(curl, CURLOPT_POSTFIELDS, data.c_str());
|
||||
curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE, data.size());
|
||||
|
||||
CURLcode res = curl_easy_perform(curl);
|
||||
if (res != CURLE_OK)
|
||||
{
|
||||
std::cerr << "curl_easy_perform() failed: " << curl_easy_strerror(res) << std::endl;
|
||||
}
|
||||
|
||||
curl_slist_free_all(headers);
|
||||
curl_easy_cleanup(curl);
|
||||
}
|
||||
|
||||
// Simple function to check internet connectivity by pinging a public DNS server
|
||||
bool internetAvailable()
|
||||
{
|
||||
CURL *curl = curl_easy_init();
|
||||
if (!curl)
|
||||
return false;
|
||||
curl_easy_setopt(curl, CURLOPT_URL, "http://1.1.1.1");
|
||||
curl_easy_setopt(curl, CURLOPT_CONNECT_ONLY, 1L);
|
||||
CURLcode res = curl_easy_perform(curl);
|
||||
curl_easy_cleanup(curl);
|
||||
return (res == CURLE_OK);
|
||||
}
|
||||
|
||||
std::string getEnv(const std::string &var, const std::string &defaultValue = "")
|
||||
{
|
||||
const char *val = std::getenv(var.c_str());
|
||||
return val == nullptr ? defaultValue : std::string(val);
|
||||
}
|
||||
|
||||
std::string hostName = getEnv("HOSTNAME");
|
||||
std::string url = getEnv("URL", "http://j-massing.de:19121/logs");
|
||||
long interval = std::stol(getEnv("INTERVAL", "60"));
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
printf("using hostname '%s'\n", hostName.c_str());
|
||||
|
||||
std::string lastIpv6;
|
||||
lastIpv6.reserve(INET6_ADDRSTRLEN);
|
||||
|
||||
while (true)
|
||||
{
|
||||
if (internetAvailable())
|
||||
{
|
||||
std::string ipv6 = getGlobalIpv6();
|
||||
if (!ipv6.empty())
|
||||
{
|
||||
send(url, hostName + "#" + ipv6);
|
||||
}
|
||||
}
|
||||
std::this_thread::sleep_for(std::chrono::seconds(interval));
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
Loading…
Add table
Reference in a new issue