Merge branch 'main' into patch-1
This commit is contained in:
commit
b4c851b39e
42
.github/workflows/cmake.yml
vendored
Normal file
42
.github/workflows/cmake.yml
vendored
Normal file
@ -0,0 +1,42 @@
|
||||
name: CMake
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ main ]
|
||||
pull_request:
|
||||
branches: [ main ]
|
||||
|
||||
env:
|
||||
# Customize the CMake build type here (Release, Debug, RelWithDebInfo, etc.)
|
||||
BUILD_TYPE: Release
|
||||
|
||||
|
||||
jobs:
|
||||
build:
|
||||
# The CMake configure and build commands are platform agnostic and should work equally
|
||||
# well on Windows or Mac. You can convert this to a matrix build if you need
|
||||
# cross-platform coverage.
|
||||
# See: https://docs.github.com/en/free-pro-team@latest/actions/learn-github-actions/managing-complex-workflows#using-a-build-matrix
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
|
||||
- name: Install xmllint
|
||||
run: sudo apt-get install libpcaudio-dev
|
||||
|
||||
- name: Configure CMake
|
||||
# Configure CMake in a 'build' subdirectory. `CMAKE_BUILD_TYPE` is only required if you are using a single-configuration generator such as make.
|
||||
# See https://cmake.org/cmake/help/latest/variable/CMAKE_BUILD_TYPE.html?highlight=cmake_build_type
|
||||
run: cmake -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}}
|
||||
|
||||
- name: Build
|
||||
# Build your program with the given configuration
|
||||
run: cmake --build ${{github.workspace}}/build --config ${{env.BUILD_TYPE}}
|
||||
|
||||
- name: Test
|
||||
working-directory: ${{github.workspace}}/build
|
||||
# Execute tests defined by the CMake configuration.
|
||||
# See https://cmake.org/cmake/help/latest/manual/ctest.1.html for more detail
|
||||
run: ctest -C ${{env.BUILD_TYPE}}
|
||||
|
@ -2,26 +2,27 @@ cmake_minimum_required(VERSION 3.1)
|
||||
|
||||
project(googerteller VERSION 1.0
|
||||
DESCRIPTION "Audible feedback on Google communications"
|
||||
LANGUAGES CXX)
|
||||
LANGUAGES CXX C)
|
||||
|
||||
|
||||
set(CMAKE_CXX_STANDARD 17 CACHE STRING "The C++ standard to use")
|
||||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||
set(CMAKE_CXX_EXTENSIONS ON)
|
||||
|
||||
#add_library(support STATIC ext/powerblog/h2o-pp.cc
|
||||
# ext/powerblog/ext/simplesocket/swrappers.cc
|
||||
# ext/powerblog/ext/simplesocket/comboaddress.cc
|
||||
# ext/powerblog/ext/simplesocket/sclasses.cc
|
||||
# ext/fmt-7.1.3/src/format.cc)
|
||||
add_custom_target(Work ALL DEPENDS configs.hh)
|
||||
|
||||
|
||||
|
||||
#target_include_directories(support PUBLIC ext/powerblog/ext/simplesocket ext/powerblog/ext ext/fmt-7.1.3/include/)
|
||||
#target_link_libraries(support PUBLIC -lh2o-evloop -lssl -lcrypto Threads::Threads)
|
||||
|
||||
add_executable(teller teller.cc )
|
||||
add_executable(teller teller.cc ext/lpm.c)
|
||||
target_link_libraries(teller -lpcaudio -lpthread)
|
||||
|
||||
#enable_testing()
|
||||
#add_test(testname testrunner)
|
||||
add_executable(testrunner testrunner.cc ext/lpm.c )
|
||||
target_link_libraries(testrunner -lpcaudio)
|
||||
|
||||
enable_testing()
|
||||
add_test(testname testrunner)
|
||||
|
||||
add_custom_command(
|
||||
OUTPUT configs.hh
|
||||
COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/make-built-in-config.sh ${CMAKE_CURRENT_SOURCE_DIR}
|
||||
DEPENDS teller.conf trackers.conf
|
||||
)
|
||||
|
||||
|
98
README.md
98
README.md
@ -5,17 +5,42 @@ Audible feedback on just how much your browsing feeds into Google.
|
||||
By bert@hubertnet.nl / https://berthub.eu/
|
||||
|
||||
Makes a little bit of noise any time your computer sends a packet to a
|
||||
Google service, which excludes Google Cloud users.
|
||||
tracker or a Google service, which excludes Google Cloud users.
|
||||
|
||||
Demo video [in this tweet](https://twitter.com/bert_hu_bert/status/1561466204602220544)
|
||||
|
||||
[Blog post with more videos](https://berthub.eu/articles/posts/tracker-beeper/)
|
||||
|
||||
## Installing on OSX:
|
||||
If need be, install Homebrew via https://brew.sh/ as this will allow you to
|
||||
compile new software. To do so, copy paste the instruction under 'Install
|
||||
Homebrew' into the terminal app ([Apple
|
||||
help](https://support.apple.com/guide/terminal/open-or-quit-terminal-apd5265185d-f365-44cb-8b09-71a064a42125/mac)). This might take quite a while.
|
||||
It will also ask for your password.
|
||||
|
||||
Then run:
|
||||
```
|
||||
brew tap robertjakub/teller
|
||||
brew install --HEAD googerteller
|
||||
```
|
||||
|
||||
To then start the noise, enter:
|
||||
```
|
||||
sudo tcpdump -nql | teller
|
||||
```
|
||||
And it should work. For the last command, it may also again ask you to enter your
|
||||
password.
|
||||
|
||||
## How to compile
|
||||
This will currently only work on Unix derived systems (like Linux, OSX and
|
||||
FreeBSD).
|
||||
|
||||
You need a C++ compiler like `gcc-c++` and CMake for compiling the binary.
|
||||
|
||||
You also need to install `libpcaudio` (`libpcaudio-dev` on Debian/Ubuntu, `pcaudiolib-devel` on Fedora/Red Hat, `pcaudiolib` on Arch/Manjaro).
|
||||
For OSX [this is a bit more work](https://github.com/espeak-ng/pcaudiolib#mac-os)
|
||||
|
||||
Then run:
|
||||
Then do:
|
||||
|
||||
```
|
||||
cmake .
|
||||
@ -23,44 +48,30 @@ make
|
||||
```
|
||||
|
||||
## How to run
|
||||
Google is so large its IPv4 and IPv6 footprint can't be handled by tcpdump,
|
||||
or at least not efficiently. Therefore we need to define an ip(6)tables
|
||||
`ipset`. This will first exclude Google Cloud, and then include all the
|
||||
other Google IP addresses.
|
||||
|
||||
Install iptables 'ipset', and run (as root) the `ipset-setup.sh` script, or
|
||||
execute:
|
||||
|
||||
Start as:
|
||||
```
|
||||
ipset create google-services hash:net
|
||||
for a in $(cat goog-cloud-prefixes.txt)
|
||||
do
|
||||
echo $a
|
||||
ipset add google-services $a nomatch
|
||||
done
|
||||
for a in $(cat goog-prefixes.txt)
|
||||
do
|
||||
ipset add google-services $a
|
||||
done
|
||||
|
||||
ipset create google-services6 hash:net family inet6
|
||||
for a in $(cat goog-cloud-prefixes6.txt)
|
||||
do
|
||||
ipset add google-services6 $a nomatch
|
||||
done
|
||||
|
||||
for a in $(cat goog-prefixes6.txt)
|
||||
do
|
||||
ipset add google-services6 $a
|
||||
done
|
||||
iptables -I OUTPUT -m set --match-set google-services dst -j NFLOG --nflog-group 20 --nflog-threshold 1
|
||||
ip6tables -I OUTPUT -m set --match-set google-services6 dst -j NFLOG --nflog-group 20 --nflog-threshold 1
|
||||
sudo tcpdump -nql | ./teller
|
||||
```
|
||||
|
||||
Then start as:
|
||||
```
|
||||
sudo tcpdump -i nflog:20 -ln | ./teller
|
||||
```
|
||||
And cry.
|
||||
|
||||
## Configuration
|
||||
Tracker data is read from `tracker.conf` which you should only edit if
|
||||
you've learned about more IP addresses for relevant trackers.
|
||||
|
||||
In `teller.conf` you can edit for each tracker where the noise should end up
|
||||
(left or right channel), and what the frequency should be.
|
||||
|
||||
## Data source
|
||||
The list of Google services IP addresses can be found on [this Google
|
||||
support page](https://support.google.com/a/answer/10026322?hl=en).
|
||||
|
||||
Note that this splits out Google services and Google cloud user IP
|
||||
addresses. However, it appears the Google services set includes the cloud IP
|
||||
addresses, so you must check both sets before determining something is in
|
||||
fact a Google service and not a Google customer.
|
||||
|
||||
# To run on a single process on Linux
|
||||
|
||||
Or, to track a single process, fe `firefox`, start it and run:
|
||||
|
||||
@ -72,11 +83,10 @@ sudo bpftrace netsendmsg.bt |
|
||||
|
||||
And cry.
|
||||
|
||||
## Data source
|
||||
The list of Google services IP addresses can be found on [this Google
|
||||
support page](https://support.google.com/a/answer/10026322?hl=en).
|
||||
## Dependencies
|
||||
This software gratefully builds on:
|
||||
|
||||
Note that this splits out Google services and Google cloud user IP
|
||||
addresses. However, it appears the Google services set includes the cloud IP
|
||||
addresses, so you must check both sets before determining something is in
|
||||
fact a Google service and not a Google customer.
|
||||
* [doctest](https://github.com/doctest/doctest) testing framework
|
||||
* [lpm](https://github.com/rmind/liblpm) Longest Prefix Match library
|
||||
* [Portable C Audio Library 1.2](https://github.com/espeak-ng/pcaudiolib) - which you need to install yourself
|
||||
* tcpdump of course
|
||||
|
6580
ext/doctest.h
Normal file
6580
ext/doctest.h
Normal file
File diff suppressed because it is too large
Load Diff
420
ext/lpm.c
Normal file
420
ext/lpm.c
Normal file
@ -0,0 +1,420 @@
|
||||
/*
|
||||
* Copyright (c) 2016 Mindaugas Rasiukevicius <rmind at noxt eu>
|
||||
* All rights reserved.
|
||||
*
|
||||
* Use is subject to license terms, as specified in the LICENSE file.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Longest Prefix Match (LPM) library supporting IPv4 and IPv6.
|
||||
*
|
||||
* Algorithm:
|
||||
*
|
||||
* Each prefix gets its own hash map and all added prefixes are saved
|
||||
* in a bitmap. On a lookup, we perform a linear scan of hash maps,
|
||||
* iterating through the added prefixes only. Usually, there are only
|
||||
* a few unique prefixes used and such simple algorithm is very efficient.
|
||||
* With many IPv6 prefixes, the linear scan might become a bottleneck.
|
||||
*/
|
||||
|
||||
#include <sys/socket.h>
|
||||
#include <arpa/inet.h>
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <inttypes.h>
|
||||
#include <string.h>
|
||||
#include <strings.h>
|
||||
#include <errno.h>
|
||||
#include <assert.h>
|
||||
|
||||
#include "lpm.h"
|
||||
|
||||
#define LPM_MAX_PREFIX (128)
|
||||
#define LPM_MAX_WORDS (LPM_MAX_PREFIX >> 5)
|
||||
#define LPM_TO_WORDS(x) ((x) >> 2)
|
||||
#define LPM_HASH_STEP (8)
|
||||
#define LPM_LEN_IDX(len) ((len) >> 4)
|
||||
|
||||
#ifdef DEBUG
|
||||
#define ASSERT assert
|
||||
#else
|
||||
#define ASSERT(x)
|
||||
#endif
|
||||
|
||||
typedef struct lpm_ent {
|
||||
struct lpm_ent *next;
|
||||
void * val;
|
||||
unsigned len;
|
||||
uint8_t key[];
|
||||
} lpm_ent_t;
|
||||
|
||||
typedef struct {
|
||||
unsigned hashsize;
|
||||
unsigned nitems;
|
||||
lpm_ent_t ** bucket;
|
||||
} lpm_hmap_t;
|
||||
|
||||
struct lpm {
|
||||
uint32_t bitmask[LPM_MAX_WORDS];
|
||||
void * defvals[2];
|
||||
lpm_hmap_t prefix[LPM_MAX_PREFIX + 1];
|
||||
};
|
||||
|
||||
static const uint32_t zero_address[LPM_MAX_WORDS];
|
||||
|
||||
lpm_t *
|
||||
lpm_create(void)
|
||||
{
|
||||
lpm_t *lpm;
|
||||
|
||||
if ((lpm = calloc(1, sizeof(lpm_t))) == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
return lpm;
|
||||
}
|
||||
|
||||
void
|
||||
lpm_clear(lpm_t *lpm, lpm_dtor_t dtor, void *arg)
|
||||
{
|
||||
for (unsigned n = 0; n <= LPM_MAX_PREFIX; n++) {
|
||||
lpm_hmap_t *hmap = &lpm->prefix[n];
|
||||
|
||||
if (!hmap->hashsize) {
|
||||
ASSERT(!hmap->bucket);
|
||||
continue;
|
||||
}
|
||||
for (unsigned i = 0; i < hmap->hashsize; i++) {
|
||||
lpm_ent_t *entry = hmap->bucket[i];
|
||||
|
||||
while (entry) {
|
||||
lpm_ent_t *next = entry->next;
|
||||
|
||||
if (dtor) {
|
||||
dtor(arg, entry->key,
|
||||
entry->len, entry->val);
|
||||
}
|
||||
free(entry);
|
||||
entry = next;
|
||||
}
|
||||
}
|
||||
free(hmap->bucket);
|
||||
hmap->bucket = NULL;
|
||||
hmap->hashsize = 0;
|
||||
hmap->nitems = 0;
|
||||
}
|
||||
if (dtor) {
|
||||
dtor(arg, zero_address, 4, lpm->defvals[0]);
|
||||
dtor(arg, zero_address, 16, lpm->defvals[1]);
|
||||
}
|
||||
memset(lpm->bitmask, 0, sizeof(lpm->bitmask));
|
||||
memset(lpm->defvals, 0, sizeof(lpm->defvals));
|
||||
}
|
||||
|
||||
void
|
||||
lpm_destroy(lpm_t *lpm)
|
||||
{
|
||||
lpm_clear(lpm, NULL, NULL);
|
||||
free(lpm);
|
||||
}
|
||||
|
||||
/*
|
||||
* fnv1a_hash: Fowler-Noll-Vo hash function (FNV-1a variant).
|
||||
*/
|
||||
static uint32_t
|
||||
fnv1a_hash(const void *buf, size_t len)
|
||||
{
|
||||
uint32_t hash = 2166136261UL;
|
||||
const uint8_t *p = buf;
|
||||
|
||||
while (len--) {
|
||||
hash ^= *p++;
|
||||
hash *= 16777619U;
|
||||
}
|
||||
return hash;
|
||||
}
|
||||
|
||||
static bool
|
||||
hashmap_rehash(lpm_hmap_t *hmap, unsigned size)
|
||||
{
|
||||
lpm_ent_t **bucket;
|
||||
unsigned hashsize;
|
||||
|
||||
for (hashsize = 1; hashsize < size; hashsize <<= 1) {
|
||||
continue;
|
||||
}
|
||||
if ((bucket = calloc(1, hashsize * sizeof(lpm_ent_t *))) == NULL) {
|
||||
return false;
|
||||
}
|
||||
for (unsigned n = 0; n < hmap->hashsize; n++) {
|
||||
lpm_ent_t *list = hmap->bucket[n];
|
||||
|
||||
while (list) {
|
||||
lpm_ent_t *entry = list;
|
||||
uint32_t hash = fnv1a_hash(entry->key, entry->len);
|
||||
const unsigned i = hash & (hashsize - 1);
|
||||
|
||||
list = entry->next;
|
||||
entry->next = bucket[i];
|
||||
bucket[i] = entry;
|
||||
}
|
||||
}
|
||||
hmap->hashsize = hashsize;
|
||||
free(hmap->bucket); // may be NULL
|
||||
hmap->bucket = bucket;
|
||||
return true;
|
||||
}
|
||||
|
||||
static lpm_ent_t *
|
||||
hashmap_insert(lpm_hmap_t *hmap, const void *key, size_t len)
|
||||
{
|
||||
const unsigned target = hmap->nitems + LPM_HASH_STEP;
|
||||
const size_t entlen = offsetof(lpm_ent_t, key[len]);
|
||||
uint32_t hash, i;
|
||||
lpm_ent_t *entry;
|
||||
|
||||
if (hmap->hashsize < target && !hashmap_rehash(hmap, target)) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
hash = fnv1a_hash(key, len);
|
||||
i = hash & (hmap->hashsize - 1);
|
||||
entry = hmap->bucket[i];
|
||||
while (entry) {
|
||||
if (entry->len == len && memcmp(entry->key, key, len) == 0) {
|
||||
return entry;
|
||||
}
|
||||
entry = entry->next;
|
||||
}
|
||||
|
||||
if ((entry = malloc(entlen)) != NULL) {
|
||||
memcpy(entry->key, key, len);
|
||||
entry->next = hmap->bucket[i];
|
||||
entry->len = len;
|
||||
|
||||
hmap->bucket[i] = entry;
|
||||
hmap->nitems++;
|
||||
}
|
||||
return entry;
|
||||
}
|
||||
|
||||
static lpm_ent_t *
|
||||
hashmap_lookup(lpm_hmap_t *hmap, const void *key, size_t len)
|
||||
{
|
||||
const uint32_t hash = fnv1a_hash(key, len);
|
||||
const unsigned i = hash & (hmap->hashsize - 1);
|
||||
lpm_ent_t *entry;
|
||||
|
||||
if (hmap->hashsize == 0) {
|
||||
return NULL;
|
||||
}
|
||||
entry = hmap->bucket[i];
|
||||
|
||||
while (entry) {
|
||||
if (entry->len == len && memcmp(entry->key, key, len) == 0) {
|
||||
return entry;
|
||||
}
|
||||
entry = entry->next;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int
|
||||
hashmap_remove(lpm_hmap_t *hmap, const void *key, size_t len)
|
||||
{
|
||||
const uint32_t hash = fnv1a_hash(key, len);
|
||||
const unsigned i = hash & (hmap->hashsize - 1);
|
||||
lpm_ent_t *prev = NULL, *entry;
|
||||
|
||||
if (hmap->hashsize == 0) {
|
||||
return -1;
|
||||
}
|
||||
entry = hmap->bucket[i];
|
||||
|
||||
while (entry) {
|
||||
if (entry->len == len && memcmp(entry->key, key, len) == 0) {
|
||||
if (prev) {
|
||||
prev->next = entry->next;
|
||||
} else {
|
||||
hmap->bucket[i] = entry->next;
|
||||
}
|
||||
free(entry);
|
||||
return 0;
|
||||
}
|
||||
prev = entry;
|
||||
entry = entry->next;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
/*
|
||||
* compute_prefix: given the address and prefix length, compute and
|
||||
* return the address prefix.
|
||||
*/
|
||||
static inline void
|
||||
compute_prefix(const unsigned nwords, const uint32_t *addr,
|
||||
unsigned preflen, uint32_t *prefix)
|
||||
{
|
||||
uint32_t addr2[4];
|
||||
|
||||
if ((uintptr_t)addr & 3) {
|
||||
/* Unaligned address: just copy for now. */
|
||||
memcpy(addr2, addr, nwords * 4);
|
||||
addr = addr2;
|
||||
}
|
||||
for (unsigned i = 0; i < nwords; i++) {
|
||||
if (preflen == 0) {
|
||||
prefix[i] = 0;
|
||||
continue;
|
||||
}
|
||||
if (preflen < 32) {
|
||||
uint32_t mask = htonl(0xffffffff << (32 - preflen));
|
||||
prefix[i] = addr[i] & mask;
|
||||
preflen = 0;
|
||||
} else {
|
||||
prefix[i] = addr[i];
|
||||
preflen -= 32;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* lpm_insert: insert the CIDR into the LPM table.
|
||||
*
|
||||
* => Returns zero on success and -1 on failure.
|
||||
*/
|
||||
int
|
||||
lpm_insert(lpm_t *lpm, const void *addr,
|
||||
size_t len, unsigned preflen, void *val)
|
||||
{
|
||||
const unsigned nwords = LPM_TO_WORDS(len);
|
||||
uint32_t prefix[nwords];
|
||||
lpm_ent_t *entry;
|
||||
ASSERT(len == 4 || len == 16);
|
||||
|
||||
if (preflen == 0) {
|
||||
/* 0-length prefix is a special case. */
|
||||
lpm->defvals[LPM_LEN_IDX(len)] = val;
|
||||
return 0;
|
||||
}
|
||||
compute_prefix(nwords, addr, preflen, prefix);
|
||||
entry = hashmap_insert(&lpm->prefix[preflen], prefix, len);
|
||||
if (entry) {
|
||||
const unsigned n = --preflen >> 5;
|
||||
lpm->bitmask[n] |= 0x80000000U >> (preflen & 31);
|
||||
entry->val = val;
|
||||
return 0;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
/*
|
||||
* lpm_remove: remove the specified prefix.
|
||||
*/
|
||||
int
|
||||
lpm_remove(lpm_t *lpm, const void *addr, size_t len, unsigned preflen)
|
||||
{
|
||||
const unsigned nwords = LPM_TO_WORDS(len);
|
||||
uint32_t prefix[nwords];
|
||||
ASSERT(len == 4 || len == 16);
|
||||
|
||||
if (preflen == 0) {
|
||||
lpm->defvals[LPM_LEN_IDX(len)] = NULL;
|
||||
return 0;
|
||||
}
|
||||
compute_prefix(nwords, addr, preflen, prefix);
|
||||
return hashmap_remove(&lpm->prefix[preflen], prefix, len);
|
||||
}
|
||||
|
||||
/*
|
||||
* lpm_lookup: find the longest matching prefix given the IP address.
|
||||
*
|
||||
* => Returns the associated value on success or NULL on failure.
|
||||
*/
|
||||
void *
|
||||
lpm_lookup(lpm_t *lpm, const void *addr, size_t len)
|
||||
{
|
||||
const unsigned nwords = LPM_TO_WORDS(len);
|
||||
unsigned i, n = nwords;
|
||||
uint32_t prefix[nwords];
|
||||
|
||||
while (n--) {
|
||||
uint32_t bitmask = lpm->bitmask[n];
|
||||
|
||||
while ((i = ffs(bitmask)) != 0) {
|
||||
const unsigned preflen = (32 * n) + (32 - --i);
|
||||
lpm_hmap_t *hmap = &lpm->prefix[preflen];
|
||||
lpm_ent_t *entry;
|
||||
|
||||
compute_prefix(nwords, addr, preflen, prefix);
|
||||
entry = hashmap_lookup(hmap, prefix, len);
|
||||
if (entry) {
|
||||
return entry->val;
|
||||
}
|
||||
bitmask &= ~(1U << i);
|
||||
}
|
||||
}
|
||||
return lpm->defvals[LPM_LEN_IDX(len)];
|
||||
}
|
||||
|
||||
/*
|
||||
* lpm_lookup_prefix: return the value associated with a prefix
|
||||
*
|
||||
* => Returns the associated value on success or NULL on failure.
|
||||
*/
|
||||
void *
|
||||
lpm_lookup_prefix(lpm_t *lpm, const void *addr, size_t len, unsigned preflen)
|
||||
{
|
||||
const unsigned nwords = LPM_TO_WORDS(len);
|
||||
uint32_t prefix[nwords];
|
||||
lpm_ent_t *entry;
|
||||
ASSERT(len == 4 || len == 16);
|
||||
|
||||
if (preflen == 0) {
|
||||
return lpm->defvals[LPM_LEN_IDX(len)];
|
||||
}
|
||||
compute_prefix(nwords, addr, preflen, prefix);
|
||||
entry = hashmap_lookup(&lpm->prefix[preflen], prefix, len);
|
||||
if (entry) {
|
||||
return entry->val;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* lpm_strtobin: convert CIDR string to the binary IP address and mask.
|
||||
*
|
||||
* => The address will be in the network byte order.
|
||||
* => Returns 0 on success or -1 on failure.
|
||||
*/
|
||||
int
|
||||
lpm_strtobin(const char *cidr, void *addr, size_t *len, unsigned *preflen)
|
||||
{
|
||||
char *p, buf[INET6_ADDRSTRLEN];
|
||||
|
||||
strncpy(buf, cidr, sizeof(buf));
|
||||
buf[sizeof(buf) - 1] = '\0';
|
||||
|
||||
if ((p = strchr(buf, '/')) != NULL) {
|
||||
const ptrdiff_t off = p - buf;
|
||||
*preflen = atoi(&buf[off + 1]);
|
||||
buf[off] = '\0';
|
||||
} else {
|
||||
*preflen = LPM_MAX_PREFIX;
|
||||
}
|
||||
|
||||
if (inet_pton(AF_INET6, buf, addr) == 1) {
|
||||
*len = 16;
|
||||
return 0;
|
||||
}
|
||||
if (inet_pton(AF_INET, buf, addr) == 1) {
|
||||
if (*preflen == LPM_MAX_PREFIX) {
|
||||
*preflen = 32;
|
||||
}
|
||||
*len = 4;
|
||||
return 0;
|
||||
}
|
||||
return -1;
|
||||
}
|
28
ext/lpm.h
Normal file
28
ext/lpm.h
Normal file
@ -0,0 +1,28 @@
|
||||
/*
|
||||
* Copyright (c) 2016 Mindaugas Rasiukevicius <rmind at noxt eu>
|
||||
* All rights reserved.
|
||||
*
|
||||
* Use is subject to license terms, as specified in the LICENSE file.
|
||||
*/
|
||||
|
||||
#ifndef _LPM_H_
|
||||
#define _LPM_H_
|
||||
|
||||
__BEGIN_DECLS
|
||||
|
||||
typedef struct lpm lpm_t;
|
||||
typedef void (*lpm_dtor_t)(void *, const void *, size_t, void *);
|
||||
|
||||
lpm_t * lpm_create(void);
|
||||
void lpm_destroy(lpm_t *);
|
||||
void lpm_clear(lpm_t *, lpm_dtor_t, void *);
|
||||
|
||||
int lpm_insert(lpm_t *, const void *, size_t, unsigned, void *);
|
||||
int lpm_remove(lpm_t *, const void *, size_t, unsigned);
|
||||
void * lpm_lookup(lpm_t *, const void *, size_t);
|
||||
void * lpm_lookup_prefix(lpm_t *, const void *, size_t, unsigned);
|
||||
int lpm_strtobin(const char *, void *, size_t *, unsigned *);
|
||||
|
||||
__END_DECLS
|
||||
|
||||
#endif
|
17169
ext/toml.hpp
Normal file
17169
ext/toml.hpp
Normal file
File diff suppressed because it is too large
Load Diff
38
lpmwrapper.hh
Normal file
38
lpmwrapper.hh
Normal file
@ -0,0 +1,38 @@
|
||||
extern "C" {
|
||||
#include "ext/lpm.h"
|
||||
}
|
||||
|
||||
class LPMWrapper
|
||||
{
|
||||
public:
|
||||
LPMWrapper()
|
||||
{
|
||||
d_lpm = lpm_create();
|
||||
}
|
||||
~LPMWrapper()
|
||||
{
|
||||
lpm_destroy(d_lpm);
|
||||
}
|
||||
|
||||
void insert(const std::string& str, void* val=(void*)1)
|
||||
{
|
||||
char addr[16];
|
||||
size_t len=sizeof(addr);
|
||||
unsigned preflen=0;
|
||||
lpm_strtobin(str.c_str(), addr, &len, &preflen);
|
||||
if(lpm_insert(d_lpm, addr, len, preflen, val) < 0)
|
||||
throw std::runtime_error("Error inserting prefix");
|
||||
}
|
||||
|
||||
void* lookup(const char*str)
|
||||
{
|
||||
char addr[16];
|
||||
size_t len=sizeof(addr);
|
||||
unsigned preflen=0;
|
||||
lpm_strtobin(str, addr, &len, &preflen);
|
||||
return lpm_lookup(d_lpm, addr, len);
|
||||
}
|
||||
|
||||
private:
|
||||
lpm_t* d_lpm;
|
||||
};
|
12
make-built-in-config.sh
Executable file
12
make-built-in-config.sh
Executable file
@ -0,0 +1,12 @@
|
||||
#!/bin/sh
|
||||
cd $1
|
||||
(
|
||||
echo \#pragma once
|
||||
echo 'constexpr char tellertoml[]=R"('
|
||||
cat teller.conf
|
||||
echo ')";'
|
||||
|
||||
echo 'constexpr char trackerstoml[]=R"('
|
||||
cat trackers.conf
|
||||
echo ')";'
|
||||
) > configs.hh
|
199
teller.cc
199
teller.cc
@ -5,57 +5,190 @@
|
||||
#include <thread>
|
||||
#include <atomic>
|
||||
#include <pcaudiolib/audio.h>
|
||||
#include "ext/toml.hpp"
|
||||
#include "lpmwrapper.hh"
|
||||
#include "configs.hh"
|
||||
|
||||
using namespace std;
|
||||
|
||||
int main()
|
||||
{
|
||||
audio_object* ao;
|
||||
|
||||
struct TrackerConf
|
||||
{
|
||||
double freq{400};
|
||||
double balance{0.5}; // 0 is left, 1 is right
|
||||
std::atomic<int64_t> counter{0};
|
||||
};
|
||||
|
||||
|
||||
void playerThread(const TrackerConf* tcptr)
|
||||
{
|
||||
auto& counter = tcptr->counter;
|
||||
audio_object* ao;
|
||||
ao=create_audio_device_object(0, "teller", "");
|
||||
if(!ao) {
|
||||
cerr<<"Unable to open audio file "<<endl;
|
||||
return 0;
|
||||
return;
|
||||
}
|
||||
|
||||
int res = audio_object_open(ao, AUDIO_OBJECT_FORMAT_S16LE, 44100, 1);
|
||||
int res = audio_object_open(ao, AUDIO_OBJECT_FORMAT_S16LE, 44100, 2);
|
||||
if(res < 0) {
|
||||
cerr<<"Error opening audio: "<<audio_object_strerror(ao, res)<<endl;
|
||||
return;
|
||||
}
|
||||
|
||||
std::atomic<int64_t> counter = 0;
|
||||
auto player = [&]() {
|
||||
vector<int16_t> data;
|
||||
int ourcounter=0;
|
||||
data.reserve(44100);
|
||||
while(counter >= 0) {
|
||||
data.clear();
|
||||
if(ourcounter < counter) {
|
||||
for(int n=0; n < 250; ++n) {
|
||||
int16_t val = 20000 * sin((n/44100.0) * 500 * 2 * M_PI);
|
||||
data.push_back(val);
|
||||
}
|
||||
ourcounter++;
|
||||
if(counter - ourcounter > 1000)
|
||||
ourcounter = counter;
|
||||
}
|
||||
else {
|
||||
for(int n=0; n < 150; ++n) {
|
||||
data.push_back(0);
|
||||
}
|
||||
}
|
||||
vector<int16_t> data;
|
||||
int ourcounter=0;
|
||||
data.reserve(44100);
|
||||
while(counter >= 0) {
|
||||
data.clear();
|
||||
if(ourcounter < counter) {
|
||||
for(int n=0; n < 250; ++n) {
|
||||
int16_t val = 20000 * sin((n/44100.0) * tcptr->freq * 2 * M_PI);
|
||||
int16_t lval = tcptr->balance * val;
|
||||
int16_t rval = (1.0-tcptr->balance) * val;
|
||||
|
||||
audio_object_write(ao, &data[0], data.size() * sizeof(decltype(data)::value_type));
|
||||
// audio_object_flush(ao);
|
||||
data.push_back(lval);
|
||||
data.push_back(rval);
|
||||
}
|
||||
ourcounter++;
|
||||
if(counter - ourcounter > 1000)
|
||||
ourcounter = counter;
|
||||
}
|
||||
else {
|
||||
for(int n=0; n < 150; ++n) {
|
||||
data.push_back(0);
|
||||
data.push_back(0);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
std::thread athread(player);
|
||||
audio_object_write(ao, &data[0], data.size() * sizeof(decltype(data)::value_type));
|
||||
// audio_object_flush(ao);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
toml::table conftbl, trackertbl;
|
||||
try
|
||||
{
|
||||
trackertbl = toml::parse_file("trackers.conf");
|
||||
conftbl = toml::parse_file("teller.conf");
|
||||
}
|
||||
catch (const toml::parse_error& err)
|
||||
{
|
||||
std::cerr << "Could not read configuration files, using built-in defaults" <<endl;
|
||||
trackertbl = toml::parse(trackerstoml);
|
||||
conftbl = toml::parse(tellertoml);
|
||||
}
|
||||
|
||||
map<string, TrackerConf> trackerdb;
|
||||
auto tellerarr = conftbl.as_table();
|
||||
for(const auto& t : *tellerarr) {
|
||||
auto& entry = trackerdb[(string)t.first];
|
||||
entry.balance = conftbl[t.first]["balance"].value_or(0.5);
|
||||
entry.freq = conftbl[t.first]["freq"].value_or(500);
|
||||
cout <<"Want to play sound for tracker "<<t.first<<", balance= "<<entry.balance<<" frequency = "<<entry.freq<<endl;
|
||||
|
||||
}
|
||||
|
||||
LPMWrapper trackspos, tracksneg;
|
||||
|
||||
auto tarr = trackertbl.as_table();
|
||||
for(const auto& t : *tarr) {
|
||||
cout<<"Defining tracker "<<t.first<<endl;
|
||||
if(trackerdb.count((string)t.first)==0) {
|
||||
cout<<"Skipping tracker "<<t.first<<", user doesn't want it"<<endl;
|
||||
continue;
|
||||
}
|
||||
auto trackerptr = &trackerdb[(string)t.first];
|
||||
auto track = trackertbl[t.first]["positive"].as_array();
|
||||
|
||||
if(track) {
|
||||
track->for_each([&trackspos, &trackerptr](auto&& el) {
|
||||
if constexpr (toml::is_string<decltype(el)>) {
|
||||
trackspos.insert(*el, (void*)trackerptr);
|
||||
}
|
||||
|
||||
});
|
||||
}
|
||||
else {
|
||||
cout<<"Not array?"<<endl;
|
||||
}
|
||||
|
||||
track = trackertbl[t.first]["negative"].as_array();
|
||||
if(track) {
|
||||
track->for_each([&tracksneg, &trackerptr](auto&& el) {
|
||||
if constexpr (toml::is_string<decltype(el)>) {
|
||||
tracksneg.insert(*el, (void*)trackerptr);
|
||||
}
|
||||
|
||||
});
|
||||
}
|
||||
else {
|
||||
cout<<"Negative "<<t.first<<" not array?"<<endl;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
for(const auto& t : trackerdb) {
|
||||
std::thread athread(playerThread, &t.second);
|
||||
athread.detach();
|
||||
}
|
||||
string line;
|
||||
while(getline(cin, line)) {
|
||||
counter++;
|
||||
|
||||
/*
|
||||
22:42:25.323984 IP 13.81.0.219.29601 > 10.0.0.3.32902: tcp 1186
|
||||
22:42:25.323997 IP 10.0.0.3.32902 > 13.81.0.219.29601: tcp 0
|
||||
22:42:25.327216 b0:95:75:c3:68:92 > ff:ff:ff:ff:ff:ff, RRCP-0x25 query
|
||||
|
||||
16:53:11.082416 IP6 2603:8000:ae00:d301:5636:9bff:fe27:6af6.59394 > 2001:41f0:782d:1::2.29603: tcp 0
|
||||
|
||||
*/
|
||||
if(line.find(" IP6 ") != string::npos) {
|
||||
auto pos = line.find('>');
|
||||
if(pos == string::npos)
|
||||
continue;
|
||||
auto pos2 = line.find('.', pos);
|
||||
if(pos2 == string::npos) continue;
|
||||
line.resize(pos2);
|
||||
string ip = line.substr(pos+2, pos2 - pos - 2);
|
||||
|
||||
if(tracksneg.lookup(&line.at(pos+2))) {
|
||||
cout<<ip<<" negative match"<<endl;
|
||||
}
|
||||
else if(auto fptr = trackspos.lookup(&line.at(pos+2))) {
|
||||
cout<<ip<<" match!"<<endl;
|
||||
((TrackerConf*)fptr)->counter++;
|
||||
}
|
||||
}
|
||||
else {
|
||||
auto pos = line.find('>');
|
||||
if(pos == string::npos)
|
||||
continue;
|
||||
|
||||
auto pos2 = line.find('.', pos);
|
||||
if(pos2 == string::npos) continue;
|
||||
pos2 = line.find('.', pos2+1);
|
||||
if(pos2 == string::npos) continue;
|
||||
pos2 = line.find('.', pos2+1);
|
||||
if(pos2 == string::npos) continue;
|
||||
pos2 = line.find_first_of(".:", pos2+1);
|
||||
if(pos2 == string::npos) continue;
|
||||
|
||||
line.resize(pos2);
|
||||
string ip=line.substr(pos+2, pos2 - pos - 2);
|
||||
// cout<<&line.at(pos+2)<<endl;
|
||||
if(tracksneg.lookup(&line.at(pos+2))) {
|
||||
cout<<ip<<" negative match"<<endl;
|
||||
}
|
||||
else if(auto fptr = trackspos.lookup(&line.at(pos+2))) {
|
||||
cout<<ip<<" match!"<<endl;
|
||||
((TrackerConf*)fptr)->counter++;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
counter = -1;
|
||||
athread.join();
|
||||
sleep(1);
|
||||
}
|
||||
|
11
teller.conf
Normal file
11
teller.conf
Normal file
@ -0,0 +1,11 @@
|
||||
[google]
|
||||
balance=0
|
||||
|
||||
[facebook]
|
||||
balance=1
|
||||
freq=1000
|
||||
|
||||
[unassorted]
|
||||
balance=0.5
|
||||
freq=1500
|
||||
|
45
testrunner.cc
Normal file
45
testrunner.cc
Normal file
@ -0,0 +1,45 @@
|
||||
#define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN
|
||||
#include "ext/doctest.h"
|
||||
#include "lpmwrapper.hh"
|
||||
using namespace std;
|
||||
|
||||
TEST_CASE("basic test") {
|
||||
LPMWrapper t;
|
||||
void* ptr = (void*)1;
|
||||
t.insert("127.0.0.1/32", ptr);
|
||||
|
||||
CHECK(t.lookup("127.0.0.1") == ptr);
|
||||
CHECK(t.lookup("127.0.0.2") == 0);
|
||||
}
|
||||
|
||||
|
||||
TEST_CASE("IPv6 test") {
|
||||
LPMWrapper t;
|
||||
void* ptr = (void*)1;
|
||||
t.insert("::1", ptr);
|
||||
|
||||
CHECK(t.lookup("::1") == ptr);
|
||||
CHECK(t.lookup("::2") == 0);
|
||||
}
|
||||
|
||||
|
||||
TEST_CASE("Mixed test") {
|
||||
LPMWrapper t;
|
||||
void* ptr = (void*)1;
|
||||
t.insert("::1", ptr);
|
||||
|
||||
ptr = (void*)2;
|
||||
t.insert("192.168.0.0/16", ptr);
|
||||
|
||||
|
||||
CHECK(t.lookup("::1") == (void*)1);
|
||||
CHECK(t.lookup("192.168.1.1") == (void*)2);
|
||||
CHECK(t.lookup("192.168.255.255") == (void*)2);
|
||||
CHECK(t.lookup("10.0.0.1") == 0);
|
||||
CHECK(t.lookup("172.16.2.3") == 0);
|
||||
CHECK(t.lookup("1.0.0.0") == 0);
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
1040
trackers.conf
Normal file
1040
trackers.conf
Normal file
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue
Block a user