Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
3 changes: 3 additions & 0 deletions micropython/cmodule/natterutils/micropython.mk
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
NATTERUTILS_MOD_DIR := $(USERMOD_DIR)
SRC_USERMOD += $(NATTERUTILS_MOD_DIR)/modnatterutils.c
CFLAGS_USERMOD += -I$(NATTERUTILS_MOD_DIR)
210 changes: 210 additions & 0 deletions micropython/cmodule/natterutils/modnatterutils.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,210 @@
#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <dirent.h>
#include <errno.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>

#if defined(__linux__)
#include <netinet/in.h>
#include <sys/syscall.h>
#include <netinet/tcp.h>
#endif

#include "py/runtime.h"
#include "py/smallint.h"

#if defined(__linux__) && defined(SYS_pidfd_open) && defined(SYS_pidfd_getfd)
#define NATTERUTILS_HAVE_REUSE_PORT
#endif

#define ARRAY_SIZE(x) (sizeof(x) / sizeof(x[0]))

#ifdef NATTERUTILS_HAVE_REUSE_PORT
static unsigned long get_inode(int port, int family) {
const char *paths[] = {
"/proc/net/tcp",
"/proc/net/tcp6",
};
char *line = NULL;
size_t len = 0;
ssize_t nread;
FILE *fp;
int i;

i = (family == AF_INET6) ? 1 : 0;

fp = fopen(paths[i], "r");
if (!fp) {
return 0;
}

nread = getline(&line, &len, fp);
if (nread < 0) {
fclose(fp);
return 0;
}

while ((nread = getline(&line, &len, fp)) != -1) {
int res, local_port, rem_port, d, state, uid, timer_run, timeout;
unsigned long rxq, txq, time_len, retr, inode;
char rem_addr[128], local_addr[128];

res = sscanf(line,
"%d: %64[0-9A-Fa-f]:%X %64[0-9A-Fa-f]:%X "
"%X %lX:%lX %X:%lX %lX %d %d %lu %*s\n",
&d, local_addr, &local_port, rem_addr, &rem_port, &state,
&txq, &rxq, &timer_run, &time_len, &retr, &uid, &timeout,
&inode);
if ((res >= 14) && (state == 10) && (local_port == port)) {
fclose(fp);
return inode;
}
}

free(line);
fclose(fp);

return 0;
}

static int get_pid_fd(unsigned long inode, pid_t *pid, int *fd) {
struct dirent *dpe;
char match[256];
DIR *dp;

dp = opendir("/proc");
if (!dp) {
return -1;
}

snprintf(match, sizeof(match) - 1, "socket:[%lu]", inode);

while ((dpe = readdir(dp))) {
char path[1024];
struct dirent *dfe;
DIR *df;

if (dpe->d_type != DT_DIR) {
continue;
}

snprintf(path, sizeof(path) - 1, "/proc/%s/fd", dpe->d_name);
df = opendir(path);
if (!df) {
continue;
}

while ((dfe = readdir(df))) {
char name[256];
int len;

if (dfe->d_type != DT_LNK) {
continue;
}

snprintf(path, sizeof(path) - 1, "/proc/%s/fd/%s", dpe->d_name,
dfe->d_name);
len = readlink(path, name, sizeof(name) - 1);
if (len < 0) {
continue;
}

name[len] = '\0';
if (strcmp(name, match) == 0) {
*fd = strtoul(dfe->d_name, NULL, 10);
*pid = strtoul(dpe->d_name, NULL, 10);
closedir(df);
closedir(dp);
return 0;
}
}

closedir(df);
}

closedir(dp);

return -1;
}

static int set_reuse_port(pid_t pid, int fd) {
const int reuse = 1;
int pfd;
int sfd;

pfd = syscall(SYS_pidfd_open, pid, 0);
if (pfd < 0) {
return -1;
}

sfd = syscall(SYS_pidfd_getfd, pfd, fd, 0);
if (sfd < 0) {
close(pfd);
return -1;
}

setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(int));
setsockopt(sfd, SOL_SOCKET, SO_REUSEPORT, &reuse, sizeof(int));

close(sfd);
close(pfd);
return 0;
}

static mp_obj_t natterutils_reuse_port(mp_obj_t port_obj) {
int types[] = {AF_INET, AF_INET6};
int result = 0;
int p;
size_t i;

p = mp_obj_get_int(port_obj);

for (i = 0; i < ARRAY_SIZE(types); i++) {
unsigned long inode;

inode = get_inode(p, types[i]);
if (inode > 0) {
pid_t pid;
int res;
int sfd;

res = get_pid_fd(inode, &pid, &sfd);
if (res == 0) {
result |= set_reuse_port(pid, sfd);
}
}
}

if (result) {
mp_raise_OSError(EINVAL);
}

return mp_const_none;
}
static MP_DEFINE_CONST_FUN_OBJ_1(natterutils_reuse_port_obj,
natterutils_reuse_port);
#endif /* NATTERUTILS_HAVE_REUSE_PORT */


static const mp_rom_map_elem_t natterutils_module_globals_table[] = {
{ MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_posix) },

#ifdef NATTERUTILS_HAVE_REUSE_PORT
{ MP_ROM_QSTR(MP_QSTR_reuse_port),
MP_ROM_PTR(&natterutils_reuse_port_obj) },
#endif /* NATTERUTILS_HAVE_REUSE_PORT */
};
static MP_DEFINE_CONST_DICT(natterutils_module_globals,
natterutils_module_globals_table);


const mp_obj_module_t natterutils_user_cmodule = {
.base = { &mp_type_module },
.globals = (mp_obj_dict_t *) &natterutils_module_globals,
};

MP_REGISTER_MODULE(MP_QSTR_natterutils, natterutils_user_cmodule);
12 changes: 12 additions & 0 deletions micropython/patch/tools_mpy_tool_py.patch
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
diff --git a/tools/mpy-tool.py b/tools/mpy-tool.py
index 69d3a6c..2240ff3 100755
--- a/tools/mpy-tool.py
+++ b/tools/mpy-tool.py
@@ -1386,6 +1386,7 @@ def read_mpy(filename):

# Compute the compiled-module escaped name.
cm_escaped_name = qstr_table[0].str.replace("/", "_")[:-3]
+ cm_escaped_name = cm_escaped_name.replace('-', '_hyphen_')

# Read the outer raw code, which will in turn read all its children.
raw_code_file_offset = reader.tell()
1 change: 1 addition & 0 deletions micropython/pymodule/_manifest.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ def __manifest_add_modules():


module('natter.py', base_path='../..')
module('natter-check.py', base_path='../../natter-check')
__manifest_add_modules()

del __manifest_add_modules
4 changes: 2 additions & 2 deletions micropython/pymodule/argparse.py
Original file line number Diff line number Diff line change
Expand Up @@ -982,7 +982,7 @@ def __init__(self,
metavar=metavar)

def __call__(self, parser, namespace, values, option_string=None):
items = _ensure_value(namespace, self.dest, []).__copy__()
items = _ensure_value(namespace, self.dest, [])[:]
items.append(values)
setattr(namespace, self.dest, items)

Expand All @@ -1008,7 +1008,7 @@ def __init__(self,
metavar=metavar)

def __call__(self, parser, namespace, values, option_string=None):
items = _ensure_value(namespace, self.dest, []).__copy__()
items = _ensure_value(namespace, self.dest, [])[:]
items.append(self.const)
setattr(namespace, self.dest, items)

Expand Down
13 changes: 13 additions & 0 deletions micropython/pymodule/socket.py
Original file line number Diff line number Diff line change
Expand Up @@ -185,6 +185,19 @@ def inet_ntoa(packed_ip):
return _usocket.inet_ntop(_posix.AF_INET, packed_ip)


def gethostbyname_ex(hostname):
res = _getaddrinfo(
hostname, "0", _posix.AF_INET, _posix.SOCK_DGRAM, 0,
_posix.AI_NUMERICSERV
)
ipstrs = [
_getnameinfo(
rec[4], _posix.NI_NUMERICHOST | _posix.NI_NUMERICSERV
)[0] for rec in res
]
return (hostname, [], ipstrs)


def _getaddrinfo(*args, **kwargs):
try:
return _posix.getaddrinfo(*args, **kwargs)
Expand Down
2 changes: 1 addition & 1 deletion natter-check/natter-check.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
import struct
import codecs

__version__ = "2.2.0"
__version__ = "2.2.1"


def fix_codecs(codec_list = ["utf-8", "idna"]):
Expand Down
48 changes: 43 additions & 5 deletions natter.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@
import threading
import subprocess

__version__ = "2.2.0"
__version__ = "2.2.1"


class Logger(object):
Expand Down Expand Up @@ -235,6 +235,8 @@ def __init__(self, stun_server_list, source_host="0.0.0.0", source_port=0,
def get_mapping(self):
first = self.stun_server_list[0]
while True:
if self.source_port:
set_reuse_port(self.source_port)
try:
return self._get_mapping()
except StunClient.ServerUnavailable as ex:
Expand Down Expand Up @@ -686,25 +688,29 @@ def _nftables_init(self):
return
except subprocess.CalledProcessError:
pass

# Priority values:
# dstnat (-100) - 5: -105
# srcnat ( 100) - 5: 95
initial_rules = (
'''
table ip natter {
chain natter_dnat { }
chain natter_snat { }
chain prerouting {
type nat hook prerouting priority dstnat-5; policy accept;
type nat hook prerouting priority -105; policy accept;
jump natter_dnat;
}
chain output {
type nat hook output priority dstnat-5; policy accept;
type nat hook output priority -105; policy accept;
jump natter_dnat;
}
chain postrouting {
type nat hook postrouting priority srcnat-5; policy accept;
type nat hook postrouting priority 95; policy accept;
jump natter_snat;
}
chain input {
type nat hook input priority srcnat-5; policy accept;
type nat hook input priority 95; policy accept;
jump natter_snat;
}
}
Expand Down Expand Up @@ -1433,6 +1439,18 @@ def search_codec(name):
codecs.register(search_codec)


def run_natter_check():
try:
modcheck = __import__('natter-check', None, None, ['natter-check'])
except ImportError:
raise RuntimeError("natter-check.py is missing") from None

if hasattr(modcheck, 'natter-check'):
modcheck = modcheck.__dict__['natter-check']

modcheck.main()


def check_docker_network():
if not sys.platform.startswith("linux"):
return
Expand Down Expand Up @@ -1541,6 +1559,19 @@ def ip_normalize(ipaddr):
return socket.inet_ntoa(socket.inet_aton(ipaddr))


def set_reuse_port(port):
try:
from natterutils import reuse_port
except ImportError:
Logger.debug("reuse-port: Not implemented on this platform. Ignored.")
return
try:
reuse_port(port)
except OSError:
Logger.debug("reuse-port: Failed to reuse port. Ignored.")
return


def natter_main(show_title = True):
argp = argparse.ArgumentParser(
description="Expose your port behind full-cone NAT to the Internet.", add_help=False
Expand All @@ -1553,6 +1584,9 @@ def natter_main(show_title = True):
group.add_argument(
"--help", action="help", help="show this help message and exit"
)
group.add_argument(
"--check", action="store_true", help="run natter-check and exit"
)
group.add_argument(
"-v", action="store_true", help="verbose mode, printing debug messages"
)
Expand Down Expand Up @@ -1630,6 +1664,10 @@ def natter_main(show_title = True):
else:
sys.tracebacklimit = 0

if args.check:
run_natter_check()
sys.exit(0)

validate_positive(interval)
if stun_list:
for stun_srv in stun_list:
Expand Down