3 (c) 2010 Gregor Riepl <onitake@gmail.com>
5 Tethering utility for Maemo
7 This program is free software: you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation, either version 3 of the License, or
10 (at your option) any later version.
12 This program is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
17 You should have received a copy of the GNU General Public License
18 along with this program. If not, see <http://www.gnu.org/licenses/>.
28 #include <sys/types.h>
29 #include <sys/sysctl.h>
31 #include <dbus/dbus.h>
34 static const char *INETDEV = "gprs0";
35 static const char *DEVICES[] = {
40 static const char *ADDRESSES[] = {
45 static const char *STARTADDRESSES[] = {
50 static const char *ENDADDRESSES[] = {
55 // Wait x seconds for process termination
56 static const unsigned int TERMINATE_WAIT = 5;
57 // Poll the main loop after x milliseconds
58 static const unsigned int POLL_MAINLOOP = 500;
60 // Run loop active flag
62 // Number of active tethering connections
63 static unsigned int active;
65 static void siginthandler(int sig) {
67 fprintf(stderr, "SIGINIT received, exiting\n");
70 fprintf(stderr, "Another SIGINIT received, aborting\n");
71 if (kill(getpid(), SIGKILL) == -1) {
72 fprintf(stderr, "PANIC! Error killing self: %s\n", strerror(errno));
77 static int launch(const char *args[]) {
80 if (execv(args[0], (char **const) args) == -1) {
81 fprintf(stderr, "Error launching external process %s: %s\n", args[0], strerror(errno));
84 } else if (pid == -1) {
85 fprintf(stderr, "Can't fork external process %s: %s\n", args[0], strerror(errno));
88 /*printf("Launching:");
90 for (a = 0; args[a]; a++) {
91 printf(" %s", args[a]);
95 if (waitpid(pid, &status, WNOHANG) == -1) {
96 fprintf(stderr, "Error waiting for child process completion: %s\n", strerror(errno));
100 for (i = 0; i < TERMINATE_WAIT && !WIFEXITED(status); i++) {
101 if (WIFEXITED(status)) {
102 if (WEXITSTATUS(status) != 0) {
103 fprintf(stderr, "Child process returned error: %d\n", WEXITSTATUS(status));
106 } else if (WIFSIGNALED(status)) {
107 fprintf(stderr, "Child process killed by signal: %d\n", WTERMSIG(status));
111 if (waitpid(pid, &status, WNOHANG) == -1) {
112 fprintf(stderr, "Error waiting for child process completion: %s\n", strerror(errno));
116 if (i >= TERMINATE_WAIT) {
117 fprintf(stderr, "Child process %s still running after %u seconds, ignoring.\n", args[0], TERMINATE_WAIT);
124 static void added(Device *device, const char *inetdev) {
125 printf("Got org.kernel.kevent.add on %s\n", device->name);
126 char *runfile = NULL;
127 if (asprintf(&runfile, "/var/run/tethering.%s.pid", device->name) == -1) {
128 fprintf(stderr, "Can't construct PID file name for device %s: %s\n", device->name, strerror(errno));
132 if (asprintf(&range, "%s,%s,3600", device->startaddress, device->endaddress) == -1) {
133 fprintf(stderr, "Can't construct address range parameter for device %s: %s\n", device->name, strerror(errno));
137 const char *ifconfig[] = { "/sbin/ifconfig", device->name, device->address, "up", NULL };
138 const char *modprobe[] = { "/sbin/modprobe", "ipt_MASQUERADE", NULL };
139 const char *iptables[] = { "/usr/sbin/iptables", "-t", "nat", "-A", "POSTROUTING", "-o", inetdev, "-j", "MASQUERADE", NULL };
140 const char *dnsmasq[] = { "/sbin/start-stop-daemon", "-S", "-p", runfile, "-b", "-x", "/usr/sbin/dnsmasq", "--", "-x", runfile, "-k", "-I", "lo", "-i", device->name, "-a", device->address, "-z", "-F", range, NULL };
141 char *forwsysctl = NULL;
142 if (asprintf(&forwsysctl, "/proc/sys/net/ipv4/conf/%s/forwarding", device->name) == -1) {
143 fprintf(stderr, "Can't construct sysctl path for device %s: %s\n", device->name, strerror(errno));
148 char *inetforwsysctl = NULL;
149 if (asprintf(&inetforwsysctl, "/proc/sys/net/ipv4/conf/%s/forwarding", inetdev) == -1) {
150 fprintf(stderr, "Can't construct inet sysctl path for device %s: %s\n", inetdev, strerror(errno));
158 if (launch(ifconfig) == 0) {
159 if (launch(modprobe) == 0) {
160 if (launch(iptables) == 0) {
161 if (launch(dnsmasq) == 0) {
162 int ffd = open(forwsysctl, O_WRONLY, 0666);
164 fprintf(stderr, "Can't enable forwarding on PAN device %s: %s\n", device->name, strerror(errno));
166 if (write(ffd, "1", 1) == -1) {
167 fprintf(stderr, "Can't enable forwarding on PAN device %s: %s\n", device->name, strerror(errno));
169 int ifd = open(inetforwsysctl, O_WRONLY, 0666);
171 fprintf(stderr, "Can't enable forwarding on WAN device %s (path %s): %s\n", inetdev, inetforwsysctl, strerror(errno));
173 if (write(ifd, "1", 1) == -1) {
174 fprintf(stderr, "Can't enable forwarding on WAN device %s: %s\n", inetdev, strerror(errno));
189 free(inetforwsysctl);
194 static void removed(Device *device, const char *inetdev) {
195 printf("Got org.kernel.kevent.remove on %s\n", device->name);
197 char *runfile = NULL;
198 if (asprintf(&runfile, "/var/run/tethering.%s.pid", device->name) == -1) {
199 fprintf(stderr, "Can't construct PID file name for device %s: %s\n", device->name, strerror(errno));
202 const char *dnsmasq[] = { "/sbin/start-stop-daemon", "-K", "-p", runfile, "-x", "/usr/sbin/dnsmasq", NULL };
203 const char *iptables[] = { "/usr/sbin/iptables", "-t", "nat", "-D", "POSTROUTING", "-o", inetdev, "-j", "MASQUERADE", NULL };
205 // Errors are ignored here: we just disable as much as we can.
207 if (unlink(runfile) == -1) {
208 fprintf(stderr, "Error removing PID file for device %s: %s\n", device->name, strerror(errno));
214 if (active > 0) active--;
216 char *inetforwsysctl = NULL;
217 if (asprintf(&inetforwsysctl, "/proc/sys/net/ipv4/conf/%s/forwarding", inetdev) == -1) {
218 fprintf(stderr, "Can't construct inet sysctl path for device %s: %s\n", inetdev, strerror(errno));
221 int fd = open(inetforwsysctl, O_WRONLY, 0666);
223 fprintf(stderr, "Can't enable forwarding on WAN device %s: %s\n", inetdev, strerror(errno));
225 if (write(fd, "0", 1) == -1) {
226 fprintf(stderr, "Can't enable forwarding on WAN device %s: %s\n", inetdev, strerror(errno));
230 free(inetforwsysctl);
234 int main(int argc, const char *argv[]) {
237 Device *devices = NULL;
240 for (i = 0; DEVICES[i]; i++) {
241 Device *device = device_new(DEVICES[i]);
243 device_set_address(device, ADDRESSES[i]);
244 device_set_startaddress(device, STARTADDRESSES[i]);
245 device_set_endaddress(device, ENDADDRESSES[i]);
246 if (device_validate(device)) {
247 node = device_append(node, device);
252 device_delete(device);
258 fprintf(stderr, "Warning, no devices configured. I will just sit here and wait for nicer weather.\n");
262 dbus_error_init(&err);
263 DBusConnection *conn = dbus_bus_get(DBUS_BUS_SYSTEM, &err);
264 if (dbus_error_is_set(&err)) {
265 fprintf(stderr, "Error %s occured: %s\n", err.name, err.message);
266 dbus_error_free(&err);
272 for (node = devices; node; node = node->next) {
274 if (!asprintf(&filter, "type='signal',interface='org.kernel.kevent',path='/org/kernel/class/net/%s'", node->name)) {
275 fprintf(stderr, "Can't construct filter rule for device %s: %s\n", node->name, strerror(errno));
277 dbus_bus_add_match(conn, filter, NULL);
281 dbus_connection_flush(conn);
284 signal(SIGINT, siginthandler);
286 dbus_connection_read_write(conn, POLL_MAINLOOP);
287 DBusMessage* msg = dbus_connection_pop_message(conn);
289 if (dbus_message_is_signal(msg, "org.kernel.kevent", "add")) {
291 if (!dbus_message_get_path_decomposed(msg, &path)) {
292 fprintf(stderr, "Can't get add message path!\n");
295 for (last = 0; path[last] && last <= 6; last++);
296 if (last == 0 || last > 6) {
297 fprintf(stderr, "Add message has no path or path too long!\n");
299 Device *device = device_search(devices, path[last - 1]);
301 added(device, INETDEV);
304 dbus_free_string_array(path);
306 } else if (dbus_message_is_signal(msg, "org.kernel.kevent", "remove")) {
308 if (!dbus_message_get_path_decomposed(msg, &path)) {
309 fprintf(stderr, "Can't get remove message path!\n");
312 for (last = 0; path[last] && last <= 6; last++);
313 if (last == 0 || last > 6) {
314 fprintf(stderr, "Remove message has no path or path too long!\n");
316 Device *device = device_search(devices, path[last - 1]);
318 removed(device, INETDEV);
321 dbus_free_string_array(path);
324 dbus_message_unref(msg);
328 device_delete_all(devices);
329 dbus_connection_unref(conn);