4 changed files with 296 additions and 0 deletions
@ -0,0 +1,8 @@
@@ -0,0 +1,8 @@
|
||||
CC=gcc
|
||||
CFLAGS=-Wall
|
||||
|
||||
udpproxy: udpproxy.c |
||||
$(CC) $(CFLAGS) -o udpproxy udpproxy.c
|
||||
|
||||
clean: |
||||
rm -f udpproxy
|
@ -0,0 +1,74 @@
@@ -0,0 +1,74 @@
|
||||
# UDP Proxy |
||||
|
||||
This is a tool to do UDP proxying, particularly for MAVLink |
||||
connections. It is useful when operating both a ground station and |
||||
aircraft on network links that don't have a public IP address. |
||||
|
||||
# Functionality |
||||
|
||||
udpproxy opens two listening UDP ports. When it has a connection on |
||||
both ports then it will forward packets between the ports. This allows |
||||
your GCS to connect to one of the ports and your aircraft to connect |
||||
to the other port. The GCS and aircraft will be able to communicate, |
||||
despite both not having public IP addresses. |
||||
|
||||
# Why not a VPN? |
||||
|
||||
udpproxy is an alternative to using a VPN for communication between |
||||
the aircraft and the GCS. The reason for not using a VPN in flight is |
||||
VPNs typically have a high reconnect time, and often add significant |
||||
latency. This poses an issue for aircraft control as you may lose the |
||||
ability to control the aircraft for minutes if there is a short |
||||
network outage. Using udpproxy minimises the time for the link to |
||||
re-establish after a network outage. |
||||
|
||||
# Disadvantages |
||||
|
||||
The main disadvantage of udpproxy is that it offers no security. If |
||||
someone knows that UDP ports and host you are using then they could |
||||
connect to your aircraft and control it. The risk can be reduced by |
||||
enabling MAVLink2 signing which allows you to ensure that nobody can |
||||
control the aircraft without knowing the signing key. |
||||
|
||||
You can also reduce the risk by using firewall rules on the computer |
||||
to run the proxy on to only allow connections from the IP ranges you |
||||
known you will be using. |
||||
|
||||
# Building |
||||
|
||||
Just run 'make' command |
||||
|
||||
# Usage |
||||
|
||||
Basic usage is: |
||||
|
||||
udpproxy PORT1 PORT2 |
||||
|
||||
this will listen on both PORT1 and PORT2. You should then make an |
||||
outgoing UDP connection from both GCS and aircraft to those ports, one |
||||
to each port. |
||||
|
||||
Adding the -v option tells udpproxy to display information about new |
||||
connections and shows transfer rates which are useful for diagnostics. |
||||
|
||||
You should run udpproxy on a computer with a public IP address. |
||||
|
||||
# Keeping it running |
||||
|
||||
You will typically want to keep udpproxy running for long periods |
||||
without having to keep a shell open on the computer running the |
||||
proxy. An example script which starts it under GNU screen and thus |
||||
allows you to monitor the connections and automatically restart them |
||||
is provided in this directory. |
||||
|
||||
# Connecting |
||||
|
||||
To connect from mavproxy to your proxy just add this to the |
||||
mavproxy.py command line: |
||||
|
||||
--out AA.BB.CC.DD:PORT1 |
||||
|
||||
where AA.BB.CC.DD is the IPv4 network address of your proxy. |
||||
|
||||
To connect from MissionPlanner use the "UDPCL" option, and enter the |
||||
IP address and port number of the proxy. |
@ -0,0 +1,19 @@
@@ -0,0 +1,19 @@
|
||||
#!/bin/bash |
||||
# an example script that starts udpproxy for multiple ports under GNU |
||||
# screen, allowing for unattended operation of the proxy for long |
||||
# periods |
||||
|
||||
killall -9 udpproxy |
||||
screen -AdmS proxy -t tab0 bash |
||||
|
||||
BASE_PORT=10401 |
||||
NUM_PORTS=10 |
||||
port=$BASE_PORT |
||||
count=$NUM_PORTS |
||||
while [ $count -gt 0 ]; do |
||||
port2=$((port+1)) |
||||
echo $port $port2 |
||||
screen -S proxy -X screen -t $port ./udpproxy $port $port2 -v |
||||
port=$((port+2)) |
||||
count=$((count-2)) |
||||
done |
@ -0,0 +1,195 @@
@@ -0,0 +1,195 @@
|
||||
/*
|
||||
UDP proxy code for connecting two UDP endpoints |
||||
Released under GNU GPLv3 |
||||
Author: Andrew Tridgell |
||||
*/ |
||||
#define _GNU_SOURCE |
||||
|
||||
#include <stdio.h> |
||||
#include <stdlib.h> |
||||
#include <string.h> |
||||
#include <time.h> |
||||
#include <errno.h> |
||||
#include <stdbool.h> |
||||
#include <sys/socket.h> |
||||
#include <netdb.h> |
||||
#include <unistd.h> |
||||
#include <stdlib.h> |
||||
#include <fcntl.h> |
||||
#include <sys/types.h> |
||||
#include <sys/time.h> |
||||
#include <arpa/inet.h> |
||||
#include <netinet/in.h> |
||||
|
||||
static bool verbose; |
||||
static int listen_port1, listen_port2; |
||||
|
||||
static double timestamp() |
||||
{ |
||||
struct timeval tval; |
||||
gettimeofday(&tval,NULL); |
||||
return tval.tv_sec + (tval.tv_usec*1.0e-6); |
||||
} |
||||
|
||||
/*
|
||||
open a socket of the specified type, port and address for incoming data |
||||
*/ |
||||
int open_socket_in(int port) |
||||
{ |
||||
struct sockaddr_in sock; |
||||
int res; |
||||
int one=1; |
||||
|
||||
memset(&sock,0,sizeof(sock)); |
||||
|
||||
#ifdef HAVE_SOCK_SIN_LEN |
||||
sock.sin_len = sizeof(sock); |
||||
#endif |
||||
sock.sin_port = htons(port); |
||||
sock.sin_family = AF_INET; |
||||
|
||||
res = socket(AF_INET, SOCK_DGRAM, 0); |
||||
if (res == -1) {
|
||||
fprintf(stderr, "socket failed\n"); return -1;
|
||||
return -1; |
||||
} |
||||
|
||||
setsockopt(res,SOL_SOCKET,SO_REUSEADDR,(char *)&one,sizeof(one)); |
||||
|
||||
if (bind(res, (struct sockaddr *)&sock, sizeof(sock)) < 0) {
|
||||
return(-1);
|
||||
} |
||||
|
||||
return res; |
||||
} |
||||
|
||||
static void main_loop(int sock1, int sock2) |
||||
{ |
||||
unsigned char buf[10240]; |
||||
bool have_conn1=false; |
||||
bool have_conn2=false; |
||||
double last_pkt1=0; |
||||
double last_pkt2=0; |
||||
int fdmax = (sock1>sock2?sock1:sock2)+1; |
||||
double last_stats = timestamp(); |
||||
uint32_t bytes_in1=0; |
||||
uint32_t bytes_in2=0; |
||||
|
||||
while (1) { |
||||
fd_set fds; |
||||
int ret; |
||||
struct timeval tval; |
||||
double now = timestamp(); |
||||
|
||||
if (verbose && now - last_stats > 1) { |
||||
double dt = now - last_stats; |
||||
printf("%u: %u bytes/sec %u: %u bytes/sec\n", |
||||
(unsigned)listen_port1, (unsigned)(bytes_in1/dt), |
||||
(unsigned)listen_port2, (unsigned)(bytes_in2/dt)); |
||||
bytes_in1 = bytes_in2 = 0; |
||||
last_stats = now; |
||||
} |
||||
|
||||
if (have_conn1 && now - last_pkt1 > 10) { |
||||
break; |
||||
} |
||||
if (have_conn2 && now - last_pkt2 > 10) { |
||||
break; |
||||
} |
||||
|
||||
FD_ZERO(&fds); |
||||
FD_SET(sock1, &fds); |
||||
FD_SET(sock2, &fds); |
||||
|
||||
tval.tv_sec = 10; |
||||
tval.tv_usec = 0; |
||||
|
||||
ret = select(fdmax, &fds, NULL, NULL, &tval); |
||||
if (ret == -1 && errno == EINTR) continue; |
||||
if (ret <= 0) break; |
||||
|
||||
now = timestamp(); |
||||
|
||||
if (FD_ISSET(sock1, &fds)) { |
||||
struct sockaddr_in from; |
||||
socklen_t fromlen = sizeof(from); |
||||
int n = recvfrom(sock1, buf, sizeof(buf), 0,
|
||||
(struct sockaddr *)&from, &fromlen); |
||||
if (n <= 0) break; |
||||
|
||||
bytes_in1 += n; |
||||
|
||||
last_pkt1 = now; |
||||
if (!have_conn1) { |
||||
if (connect(sock1, (struct sockaddr *)&from, fromlen) != 0) { |
||||
break; |
||||
} |
||||
have_conn1 = true; |
||||
printf("have conn1\n"); |
||||
} |
||||
if (have_conn2) { |
||||
if (send(sock2, buf, n, 0) != n) { |
||||
break; |
||||
} |
||||
} |
||||
} |
||||
|
||||
if (FD_ISSET(sock2, &fds)) { |
||||
struct sockaddr_in from; |
||||
socklen_t fromlen = sizeof(from); |
||||
int n = recvfrom(sock2, buf, sizeof(buf), 0,
|
||||
(struct sockaddr *)&from, &fromlen); |
||||
if (n <= 0) break; |
||||
|
||||
bytes_in2 += n; |
||||
|
||||
last_pkt2 = now; |
||||
if (!have_conn2) { |
||||
if (connect(sock2, (struct sockaddr *)&from, fromlen) != 0) { |
||||
break; |
||||
} |
||||
have_conn2 = true; |
||||
printf("have conn2\n"); |
||||
} |
||||
if (have_conn1) { |
||||
if (send(sock1, buf, n, 0) != n) { |
||||
break; |
||||
} |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
int main(int argc, char *argv[]) |
||||
{ |
||||
int sock_in1, sock_in2; |
||||
|
||||
if (argc < 3) { |
||||
printf("Usage: udpproxy <port1> <port2>\n"); |
||||
exit(1); |
||||
} |
||||
if (argc > 3) { |
||||
verbose = strcmp(argv[3],"-v") == 0; |
||||
printf("verbose=%u\n", (unsigned)verbose); |
||||
} |
||||
|
||||
while (true) { |
||||
listen_port1 = atoi(argv[1]); |
||||
listen_port2 = atoi(argv[2]); |
||||
|
||||
printf("Opening sockets %u %u\n", listen_port1, listen_port2); |
||||
sock_in1 = open_socket_in(listen_port1); |
||||
sock_in2 = open_socket_in(listen_port2); |
||||
if (sock_in1 == -1 || sock_in2 == -1) { |
||||
fprintf(stderr,"sock on ports %d or %d failed - %s\n",
|
||||
listen_port1, listen_port2, strerror(errno)); |
||||
exit(1); |
||||
} |
||||
|
||||
main_loop(sock_in1, sock_in2); |
||||
close(sock_in1); |
||||
close(sock_in2); |
||||
} |
||||
|
||||
return 0; |
||||
} |
Loading…
Reference in new issue