Initial checkin of tethering script and C implementation with hardcoded paths
authorGregor Riepl <onitake@gmail.com>
Tue, 20 Jul 2010 20:43:21 +0000 (22:43 +0200)
committerGregor Riepl <onitake@gmail.com>
Tue, 20 Jul 2010 20:43:21 +0000 (22:43 +0200)
.gitignore [new file with mode: 0644]
Makefile [new file with mode: 0644]
main.c [new file with mode: 0644]
tethering.sh [new file with mode: 0644]

diff --git a/.gitignore b/.gitignore
new file mode 100644 (file)
index 0000000..0001171
--- /dev/null
@@ -0,0 +1,2 @@
+*.o
+maemo-tethering
diff --git a/Makefile b/Makefile
new file mode 100644 (file)
index 0000000..d60af75
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,27 @@
+MAD = mad
+CC = gcc
+CFLAGS = -Wall -O2 -D_GNU_SOURCE
+INCLUDES = -I/usr/include/dbus-1.0 -I/usr/lib/dbus-1.0/include
+LIBS = -ldbus-1 -lpthread
+SSH_ADDRESS = developer@192.168.253.254
+SSH_SCP = scp
+SSH_SSH = ssh
+SSH_PATH = /home/developer
+
+maemo-tethering: main.o
+       $(MAD) $(CC) $(LDLFAGS) $(LIBS) -o $@ $^
+
+%PHONY: copy run clean
+
+copy: maemo-tethering
+       $(SSH_SCP) $^ $(SSH_ADDRESS):$(SSH_PATH)
+
+run: copy
+       $(SSH_SSH) $(SSH_ADDRESS) $(SSH_PATH)/maemo-tethering
+
+clean:
+       rm -f *.o maemo-tethering
+
+%.o: %.c
+       $(MAD) $(CC) $(CFLAGS) $(INCLUDES) -o $@ -c $^
+
diff --git a/main.c b/main.c
new file mode 100644 (file)
index 0000000..b3bb6c4
--- /dev/null
+++ b/main.c
@@ -0,0 +1,309 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <signal.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/sysctl.h>
+#include <sys/wait.h>
+#include <dbus/dbus.h>
+//#include <hal/libhal.h>
+
+static const char *INETDEV = "gprs0";
+static const char *DEVICES[] = {
+       "bnep0",
+       "usb0",
+       NULL
+};
+static const char *ADDRESSES[] = {
+       "192.168.253.254",
+       "192.168.254.254",
+       NULL
+};
+static const char *STARTADDRESSES[] = {
+       "192.168.253.1",
+       "192.168.254.1",
+       NULL
+};
+static const char *ENDADDRESSES[] = {
+       "192.168.253.254",
+       "192.168.254.254",
+       NULL
+};
+
+static int running;
+static unsigned int active;
+
+static void sigIntHandler(int sig) {
+       fprintf(stderr, "SIGINIT received, exiting\n");
+       running = 0;
+}
+
+/*static void deviceAdded(LibHalContext *ctx, const char *udi) {
+       DBusError err;
+       dbus_error_init(&err);
+       char *device = libhal_device_get_property_string(ctx, udi, "net.interface", &err);
+       if (!device) {
+               if (dbus_error_is_set(&err)) {
+                       fprintf(stderr, "Error %s occured: %s\n", err.name, err.message);
+                       dbus_error_free(&err);
+               }
+       }
+       printf("Device %s added.\n", device);
+}
+
+static void deviceRemoved(LibHalContext *ctx, const char *udi) {
+       DBusError err;
+       dbus_error_init(&err);
+       char *device = libhal_device_get_property_string(ctx, udi, "net.interface", &err);
+       if (!device) {
+               if (dbus_error_is_set(&err)) {
+                       fprintf(stderr, "Error %s occured: %s\n", err.name, err.message);
+                       dbus_error_free(&err);
+               }
+       }
+       printf("Device %s removed.\n", device);
+}*/
+
+static int launch(const char *args[]) {
+       pid_t pid = fork();
+       if (pid == 0) {
+               if (execv(args[0], (char **const) args) == -1) {
+                       fprintf(stderr, "Error launching external process %s: %s\n", args[0], strerror(errno));
+                       exit(1);
+               }
+       } else if (pid == -1) {
+               fprintf(stderr, "Can't fork external process %s: %s\n", args[0], strerror(errno));
+               return -1;
+       } else {
+               int status = 0;
+               if (waitpid(pid, &status, 0) == -1) {
+                       perror("Error waiting for child process completion");
+                       return -1;
+               }
+               if (WIFEXITED(status)) {
+                       if (WEXITSTATUS(status) != 0) {
+                               fprintf(stderr, "Child process returned error: %d\n", WEXITSTATUS(status));
+                               return -1;
+                       }
+               } else if (WIFSIGNALED(status)) {
+                       fprintf(stderr, "Child process killed by signal: %d\n", WTERMSIG(status));
+                       return -1;
+               }
+       }
+       return 0;
+}
+
+static void added(const char *device, const char *inetdev, const char *address, const char *startaddress, const char *endaddress) {
+       printf("Got org.kernel.kevent.add on %s\n", device);
+       const char *ifconfig[] = { "/sbin/ifconfig", device, address, "up", NULL };
+       launch(ifconfig);
+       const char *modprobe[] = { "/sbin/modprobe", "ipt_MASQUERADE", NULL };
+       launch(modprobe);
+       const char *iptables[] = { "/usr/sbin/iptables", "-t", "nat", "-A", "POSTROUTING", "-o", inetdev, "-j", "MASQUERADE", NULL };
+       launch(iptables);
+       char *runfile = NULL;
+       asprintf(&runfile, "/var/run/tethering.%s.pid", device);
+       char *range = NULL;
+       asprintf(&range, "%s-%s,3600", startaddress, endaddress);
+       const char *dnsmasq[] = { "/sbin/start-stop-daemon", "-S", "-p", runfile, "-m", "-b", "-x", "/usr/sbin/dnsmasq", "--", "-k", "-I", "lo", "-i", device, "-a", address, "-z", "-F", range, NULL };
+       launch(dnsmasq);
+       free(range);
+       free(runfile);
+       char *forwsysctl = NULL;
+       asprintf(&forwsysctl, "/proc/sys/net/ipv4/conf/%s/forwarding", device);
+       int fd = open(forwsysctl, O_WRONLY, 0666);
+       if (fd < 0) {
+               perror("Can't enable forwarding on PAN device");
+       } else {
+               if (write(fd, "0", 1) == -1) {
+                       perror("Can't enable forwarding on PAN device");
+               }
+               close(fd);
+       }
+       free(forwsysctl);
+       asprintf(&forwsysctl, "/proc/sys/net/ipv4/conf/%s/forwarding", inetdev);
+       fd = open(forwsysctl, O_WRONLY, 0666);
+       if (fd < 0) {
+               perror("Can't enable forwarding on WAN device");
+       } else {
+               if (write(fd, "0", 1) == -1) {
+                       perror("Can't enable forwarding on WAN device");
+               }
+               close(fd);
+       }
+       free(forwsysctl);
+       active++;
+}
+
+static void removed(const char *device, const char *inetdev) {
+       printf("Got org.kernel.kevent.remove on %s\n", device);
+       char *runfile = NULL;
+       asprintf(&runfile, "/var/run/tethering.%s.pid", device);
+       const char *dnsmasq[] = { "/sbin/start-stop-daemon", "-K", "-p", runfile, "-x", "/usr/sbin/dnsmasq", NULL };
+       launch(dnsmasq);
+       free(runfile);
+       const char *iptables[] = { "/usr/sbin/iptables", "-t", "nat", "-D", "POSTROUTING", "-o", inetdev, "-j", "MASQUERADE", NULL };
+       launch(iptables);
+       if (active > 0) active--;
+       if (active == 0) {
+               char *forwsysctl = NULL;
+               asprintf(&forwsysctl, "/proc/sys/net/ipv4/conf/%s/forwarding", inetdev);
+               int fd = open(forwsysctl, O_WRONLY, 0666);
+               if (fd < 0) {
+                       perror("Can't disable forwarding on WAN device");
+               } else {
+                       if (write(fd, "0", 1) == -1) {
+                               perror("Can't disable forwarding on WAN device");
+                       }
+                       close(fd);
+               }
+               free(forwsysctl);
+       }
+}
+
+static int finddev(const char* device, const char *devices[]) {
+       int i;
+       for (i = 0; devices[i]; i++) {
+               if (strcmp(device, devices[i]) == 0) {
+                       return i;
+               }
+       }
+       return -1;
+}
+
+int main(int argc, const char *argv[]) {
+       running = 1;
+       active = 0;
+       signal(SIGINT, sigIntHandler);
+
+       DBusError err;
+       dbus_error_init(&err);
+       DBusConnection *conn = dbus_bus_get(DBUS_BUS_SYSTEM, &err);
+       if (dbus_error_is_set(&err)) {
+               fprintf(stderr, "Error %s occured: %s\n", err.name, err.message);
+               dbus_error_free(&err);
+       }
+       if (!conn) {
+               return 1;
+       }
+       /*LibHalContext *ctx = libhal_ctx_new();
+       if (!ctx) {
+               fprintf(stderr, "Error occured: Can't create HAL context.\n");
+               return 2;
+       }
+       if (!libhal_ctx_set_dbus_connection(ctx, conn)) {
+               fprintf(stderr, "Error occured: Can't assign DBUS connection to HAL context.\n");
+               return 3;
+       }
+       if (!libhal_ctx_set_device_added(ctx, deviceAdded)) {
+               fprintf(stderr, "Error occured: Can't set device added message handler.\n");
+               return 4;
+       }
+       if (!libhal_ctx_set_device_removed(ctx, deviceRemoved)) {
+               fprintf(stderr, "Error occured: Can't set device removed message handler.\n");
+               return 4;
+       }
+       if (!libhal_ctx_init(ctx, &err)) {
+               if (dbus_error_is_set(&err)) {
+                       fprintf(stderr, "Error %s occured: %s\n", err.name, err.message);
+                       dbus_error_free(&err);
+               }
+               return 5;
+       }
+       dbus_bool_t nodisc = TRUE;
+       while (running && nodisc) {
+               nodisc = dbus_connection_read_write_dispatch(conn, 500);
+       }*/
+       //dbus_bus_add_match(conn, "type='signal',interface='org.freedesktop.Hal.Manager',member='DeviceAdded'", NULL);
+       //dbus_bus_add_match(conn, "type='signal',interface='org.freedesktop.Hal.Manager',member='DeviceRemoved'", NULL);
+       //dbus_bus_add_match(conn, "type='signal',interface='org.kernel.kevent',path='/org/kernel/class/net/bnep0'", NULL);
+       //dbus_bus_add_match(conn, "type='signal',interface='org.kernel.kevent',path='/org/kernel/class/net/usb0'", NULL);
+       int i;
+       for (i = 0; DEVICES[i]; i++) {
+               char *filter = NULL;
+               if (!asprintf(&filter, "type='signal',interface='org.kernel.kevent',path='/org/kernel/class/net/%s'", DEVICES[i])) {
+                       perror("Can't construct filter rule");
+               } else {
+                       dbus_bus_add_match(conn, filter, NULL);
+               }
+       }
+       dbus_connection_flush(conn);
+       while (running) {
+               dbus_connection_read_write(conn, 500);
+               DBusMessage* msg = dbus_connection_pop_message(conn);
+               if (msg) {
+                       /*if (dbus_message_is_signal(msg, "org.freedesktop.Hal.Manager", "DeviceAdded")) {
+                               DBusMessageIter args;
+                               if (!dbus_message_iter_init(msg, &args)) {
+                                       fprintf(stderr, "Message has no arguments!\n");
+                               } else if (dbus_message_iter_get_arg_type(&args) != DBUS_TYPE_STRING) {
+                                       fprintf(stderr, "Argument is not a string!\n");
+                               } else {
+                                       char *sigvalue = NULL;
+                                       dbus_message_iter_get_basic(&args, &sigvalue);
+                                       fprintf(stderr, "Got DeviceAdded with value %s\n", sigvalue);
+                               }
+                       } else if (dbus_message_is_signal(msg, "org.freedesktop.Hal.Manager", "DeviceRemoved")) {
+                               DBusMessageIter args;
+                               if (!dbus_message_iter_init(msg, &args)) {
+                                       fprintf(stderr, "Message has no arguments!\n");
+                               } else if (dbus_message_iter_get_arg_type(&args) != DBUS_TYPE_STRING) {
+                                       fprintf(stderr, "Argument is not a string!\n");
+                               } else {
+                                       char *sigvalue = NULL;
+                                       dbus_message_iter_get_basic(&args, &sigvalue);
+                                       fprintf(stderr, "Got DeviceRemoved with value %s\n", sigvalue);
+                               }
+                       } else */
+                       if (dbus_message_is_signal(msg, "org.kernel.kevent", "add")) {
+                               char **path = NULL;
+                               if (!dbus_message_get_path_decomposed(msg, &path)) {
+                                       fprintf(stderr, "Can't get add message path!\n");
+                               } else {
+                                       unsigned int last;
+                                       for (last = 0; path[last] && last <= 6; last++);
+                                       if (last == 0 || last > 6) {
+                                               fprintf(stderr, "Add message has no path or path too long!\n");
+                                       } else {
+                                               int index = finddev(path[last - 1], DEVICES);
+                                               if (index >= 0) {
+                                                       
+                                                       added(DEVICES[index], INETDEV, ADDRESSES[index], STARTADDRESSES[index], ENDADDRESSES[index]);
+                                               }
+                                       }
+                                       dbus_free_string_array(path);
+                               }
+                       } else if (dbus_message_is_signal(msg, "org.kernel.kevent", "remove")) {
+                               char **path = NULL;
+                               if (!dbus_message_get_path_decomposed(msg, &path)) {
+                                       fprintf(stderr, "Can't get remove message path!\n");
+                               } else {
+                                       unsigned int last;
+                                       for (last = 0; path[last] && last <= 6; last++);
+                                       if (last == 0 || last > 6) {
+                                               fprintf(stderr, "Remove message has no path or path too long!\n");
+                                       } else {
+                                               int index = finddev(path[last - 1], DEVICES);
+                                               if (index >= 0) {
+                                                       removed(DEVICES[index], INETDEV);
+                                               }
+                                       }
+                                       dbus_free_string_array(path);
+                               }
+                       }
+                       dbus_message_unref(msg);
+               }
+       }
+       /*if (!libhal_ctx_shutdown(ctx, &err)) {
+               if (dbus_error_is_set(&err)) {
+                       fprintf(stderr, "Error %s occured: %s\n", err.name, err.message);
+                       dbus_error_free(&err);
+               }
+       }
+       libhal_ctx_free(ctx);*/
+       dbus_connection_unref(conn);
+       return 0;
+}
diff --git a/tethering.sh b/tethering.sh
new file mode 100644 (file)
index 0000000..bf40abc
--- /dev/null
@@ -0,0 +1,67 @@
+#!/bin/sh
+#
+# /usr/bin/tethering.sh
+#
+# Enable tethering on USB network and Bluetooth PAN.
+#
+# Note that the INETDEV must be up and running when this script is
+# started, or forwarding will not be enabled.
+# Having tethering on multiple devices concurrently is possible,
+# but forwarding will be disabled once the first device disconnetcs.
+# A better check is necessary.
+#
+# Put this into /etc/dbus-scripts/tethering :
+# /usr/bin/tethering.sh * * org.freedesktop.Hal.Manager DeviceAdded
+# /usr/bin/tethering.sh * * org.freedesktop.Hal.Manager DeviceRemoved
+#
+# dbus-scripts doesn't give us the event path, so we have to use a more
+# convoluted method of getting the network interface.
+#
+
+EVENT="$4"
+UDI="$5"
+HALDEV="$(echo $UDI | sed 's#.*/\([0-9a-zA-Z_]*\)#\1#')"
+LOG="/tmp/tethering.log"
+INETDEV="gprs0"
+RUNFILE="/var/run/tethering.$HALDEV.pid"
+IFACEFILE="/var/run/tethering.$HALDEV.iface"
+
+if [ "$EVENT" = "DeviceAdded" ]; then
+       IFACE=$(hal-get-property --udi $UDI --key net.interface)
+       case $IFACE in
+               bnep0 )
+                       IF_ADDRESS=192.168.254.254
+                       IF_RANGE=192.168.254.1,192.168.254.254
+               ;;
+               usb0 )
+                       IF_ADDRESS=192.168.253.254
+                       IF_RANGE=192.168.253.1,192.168.253.254
+               ;;
+               * )
+                       exit 0
+               ;;
+       esac
+
+       echo "$(date) Enabling tethering on interface $IFACE" >> $LOG
+       echo "$IFACE" > $IFACEFILE
+       ifconfig "$IFACE" "$IF_ADDRESS"
+       /sbin/modprobe ipt_MASQUERADE
+       /usr/sbin/iptables -t nat -A POSTROUTING -o $INETDEV -j MASQUERADE
+       start-stop-daemon -S -p "$RUNFILE" -m -b -x /usr/sbin/dnsmasq -- -k -I lo -i "$IFACE" -a $IF_ADDRESS -z -F $IF_RANGE,3600
+       echo 1 > /proc/sys/net/ipv4/conf/$IFACE/forwarding
+       echo 1 > /proc/sys/net/ipv4/conf/$INETDEV/forwarding
+fi
+
+if [ "$EVENT" = "DeviceRemoved" ]; then
+       if [ ! -f "$IFACEFILE" ]; then
+               exit 0
+       fi
+       IFACE="$(cat $IFACEFILE)"
+       rm "$IFACEFILE"
+       echo "$(date) Disabling tethering on device $IFACE" >> $LOG
+       echo 0 > /proc/sys/net/ipv4/conf/$INETDEV/forwarding
+       start-stop-daemon -K -p "$RUNFILE" -x /usr/sbin/dnsmasq
+       /usr/sbin/iptables -t nat -D POSTROUTING -o $INETDEV -j MASQUERADE
+fi
+
+exit 0