Added readme
[mtetherd] / main.c
1 /*
2 maemo-tethering
3 (c) 2010 Gregor Riepl <onitake@gmail.com>
4
5 Tethering utility for Maemo
6
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.
11
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.
16
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/>.
19 */
20
21 #include <stdio.h>
22 #include <stdlib.h>
23 #include <string.h>
24 #include <signal.h>
25 #include <unistd.h>
26 #include <fcntl.h>
27 #include <errno.h>
28 #include <sys/types.h>
29 #include <sys/sysctl.h>
30 #include <sys/wait.h>
31 #include <dbus/dbus.h>
32 #include "device.h"
33
34 static const char *INETDEV = "gprs0";
35 static const char *DEVICES[] = {
36         "bnep0",
37         "usb0",
38         NULL
39 };
40 static const char *ADDRESSES[] = {
41         "192.168.253.254",
42         "192.168.254.254",
43         NULL
44 };
45 static const char *STARTADDRESSES[] = {
46         "192.168.253.1",
47         "192.168.254.1",
48         NULL
49 };
50 static const char *ENDADDRESSES[] = {
51         "192.168.253.254",
52         "192.168.254.254",
53         NULL
54 };
55
56 // Run loop active flag
57 static int running;
58 // Number of active tethering connections
59 static unsigned int active;
60
61 static void siginthandler(int sig) {
62         fprintf(stderr, "SIGINIT received, exiting\n");
63         running = 0;
64 }
65
66 static int launch(const char *args[]) {
67         pid_t pid = fork();
68         if (pid == 0) {
69                 if (execv(args[0], (char **const) args) == -1) {
70                         fprintf(stderr, "Error launching external process %s: %s\n", args[0], strerror(errno));
71                         exit(1);
72                 }
73         } else if (pid == -1) {
74                 fprintf(stderr, "Can't fork external process %s: %s\n", args[0], strerror(errno));
75                 return -1;
76         } else {
77                 int status = 0;
78                 if (waitpid(pid, &status, 0) == -1) {
79                         perror("Error waiting for child process completion");
80                         return -1;
81                 }
82                 if (WIFEXITED(status)) {
83                         if (WEXITSTATUS(status) != 0) {
84                                 fprintf(stderr, "Child process returned error: %d\n", WEXITSTATUS(status));
85                                 return -1;
86                         }
87                 } else if (WIFSIGNALED(status)) {
88                         fprintf(stderr, "Child process killed by signal: %d\n", WTERMSIG(status));
89                         return -1;
90                 }
91         }
92         return 0;
93 }
94
95 static void added(Device *device, const char *inetdev) {
96         printf("Got org.kernel.kevent.add on %s\n", device->name);
97         char *runfile = NULL;
98         if (asprintf(&runfile, "/var/run/tethering.%s.pid", device->name) == -1) {
99                 fprintf(stderr, "Can't construct PID file name for device %s: %s\n", device->name, strerror(errno));
100                 return;
101         }
102         char *range = NULL;
103         if (asprintf(&range, "%s-%s,3600", device->startaddress, device->endaddress) == -1) {
104                 fprintf(stderr, "Can't construct address range parameter for device %s: %s\n", device->name, strerror(errno));
105                 free(runfile);
106                 return;
107         }
108         const char *ifconfig[] = { "/sbin/ifconfig", device->name, device->address, "up", NULL };
109         const char *modprobe[] = { "/sbin/modprobe", "ipt_MASQUERADE", NULL };
110         const char *iptables[] = { "/usr/sbin/iptables", "-t", "nat", "-A", "POSTROUTING", "-o", inetdev, "-j", "MASQUERADE", NULL };
111         const char *dnsmasq[] = { "/sbin/start-stop-daemon", "-S", "-p", runfile, "-m", "-b", "-x", "/usr/sbin/dnsmasq", "--", "-k", "-I", "lo", "-i", device->name, "-a", device->address, "-z", "-F", range, NULL };
112         char *forwsysctl = NULL;
113         if (asprintf(&forwsysctl, "/proc/sys/net/ipv4/conf/%s/forwarding", device->name) == -1) {
114                 fprintf(stderr, "Can't construct sysctl path for device %s: %s\n", device->name, strerror(errno));
115                 free(runfile);
116                 free(range);
117                 return;
118         }
119         char *inetforwsysctl = NULL;
120         if (asprintf(&inetforwsysctl, "/proc/sys/net/ipv4/conf/%s/forwarding", inetdev) == -1) {
121                 fprintf(stderr, "Can't construct inet sysctl path for device %s: %s\n", inetdev, strerror(errno));
122                 free(runfile);
123                 free(range);
124                 free(forwsysctl);
125                 return;
126         }
127
128
129         if (launch(ifconfig) == 0) {
130                 if (launch(modprobe) == 0) {
131                         if (launch(iptables) == 0) {
132                                 if (launch(dnsmasq) == 0) {
133                                         int ffd = open(forwsysctl, O_WRONLY, 0666);
134                                         if (ffd < 0) {
135                                                 fprintf(stderr, "Can't enable forwarding on PAN device %s: %s\n", device->name, strerror(errno));
136                                         } else {
137                                                 if (write(ffd, "1", 1) == -1) {
138                                                         fprintf(stderr, "Can't enable forwarding on PAN device %s: %s\n", device->name, strerror(errno));
139                                                 } else {
140                                                         int ifd = open(inetforwsysctl, O_WRONLY, 0666);
141                                                         if (ifd < 0) {
142                                                                 fprintf(stderr, "Can't enable forwarding on WAN device %s (path %s): %s\n", inetdev, inetforwsysctl, strerror(errno));
143                                                         } else {
144                                                                 if (write(ifd, "1", 1) == -1) {
145                                                                         fprintf(stderr, "Can't enable forwarding on WAN device %s: %s\n", inetdev, strerror(errno));
146                                                                 }
147                                                                 close(ifd);
148                                                         }
149                                                 }
150                                                 close(ffd);
151                                         }
152                                 }
153                         }
154                 }
155         }
156
157         free(range);
158         free(runfile);
159         free(forwsysctl);
160         free(inetforwsysctl);
161
162         active++;
163 }
164
165 static void removed(Device *device, const char *inetdev) {
166         printf("Got org.kernel.kevent.remove on %s\n", device->name);
167
168         char *runfile = NULL;
169         if (asprintf(&runfile, "/var/run/tethering.%s.pid", device->name) == -1) {
170                 fprintf(stderr, "Can't construct PID file name for device %s: %s\n", device->name, strerror(errno));
171                 return;
172         }
173         const char *dnsmasq[] = { "/sbin/start-stop-daemon", "-K", "-p", runfile, "-x", "/usr/sbin/dnsmasq", NULL };
174         const char *iptables[] = { "/usr/sbin/iptables", "-t", "nat", "-D", "POSTROUTING", "-o", inetdev, "-j", "MASQUERADE", NULL };
175
176         // Errors are ignored here: we just disable as much as we can.
177         launch(dnsmasq);
178         launch(iptables);
179
180         free(runfile);
181
182         if (active > 0) active--;
183         if (active == 0) {
184                 char *inetforwsysctl = NULL;
185                 if (asprintf(&inetforwsysctl, "/proc/sys/net/ipv4/conf/%s/forwarding", inetdev) == -1) {
186                         fprintf(stderr, "Can't construct inet sysctl path for device %s: %s\n", inetdev, strerror(errno));
187                         return;
188                 }
189                 int fd = open(inetforwsysctl, O_WRONLY, 0666);
190                 if (fd < 0) {
191                         fprintf(stderr, "Can't enable forwarding on WAN device %s: %s\n", inetdev, strerror(errno));
192                 } else {
193                         if (write(fd, "0", 1) == -1) {
194                                 fprintf(stderr, "Can't enable forwarding on WAN device %s: %s\n", inetdev, strerror(errno));
195                         }
196                         close(fd);
197                 }
198                 free(inetforwsysctl);
199         }
200 }
201
202 int main(int argc, const char *argv[]) {
203         running = 1;
204         active = 0;
205         signal(SIGINT, siginthandler);
206
207         Device *devices = NULL;
208         Device *node = NULL;
209         size_t i;
210         for (i = 0; DEVICES[i]; i++) {
211                 Device *device = device_new(DEVICES[i]);
212                 if (device) {
213                         device_set_address(device, ADDRESSES[i]);
214                         device_set_startaddress(device, STARTADDRESSES[i]);
215                         device_set_endaddress(device, ENDADDRESSES[i]);
216                         if (device_validate(device)) {
217                                 node = device_append(node, device);
218                                 if (!devices) {
219                                         devices = node;
220                                 }
221                         } else {
222                                 device_delete(device);
223                         }
224                 }
225         }
226
227         if (!devices) {
228                 fprintf(stderr, "Warning, no devices configured. I will just sit here and wait for nicer weather.\n");
229         }
230
231         DBusError err;
232         dbus_error_init(&err);
233         DBusConnection *conn = dbus_bus_get(DBUS_BUS_SYSTEM, &err);
234         if (dbus_error_is_set(&err)) {
235                 fprintf(stderr, "Error %s occured: %s\n", err.name, err.message);
236                 dbus_error_free(&err);
237         }
238         if (!conn) {
239                 return 1;
240         }
241
242         for (node = devices; node; node = node->next) {
243                 char *filter = NULL;
244                 if (!asprintf(&filter, "type='signal',interface='org.kernel.kevent',path='/org/kernel/class/net/%s'", node->name)) {
245                         fprintf(stderr, "Can't construct filter rule for device %s: %s\n", node->name, strerror(errno));
246                 } else {
247                         dbus_bus_add_match(conn, filter, NULL);
248                         free(filter);
249                 }
250         }
251         dbus_connection_flush(conn);
252
253         while (running) {
254                 dbus_connection_read_write(conn, 500);
255                 DBusMessage* msg = dbus_connection_pop_message(conn);
256                 if (msg) {
257                         if (dbus_message_is_signal(msg, "org.kernel.kevent", "add")) {
258                                 char **path = NULL;
259                                 if (!dbus_message_get_path_decomposed(msg, &path)) {
260                                         fprintf(stderr, "Can't get add message path!\n");
261                                 } else {
262                                         unsigned int last;
263                                         for (last = 0; path[last] && last <= 6; last++);
264                                         if (last == 0 || last > 6) {
265                                                 fprintf(stderr, "Add message has no path or path too long!\n");
266                                         } else {
267                                                 Device *device = device_search(devices, path[last - 1]);
268                                                 if (device) {
269                                                         added(device, INETDEV);
270                                                 }
271                                         }
272                                         dbus_free_string_array(path);
273                                 }
274                         } else if (dbus_message_is_signal(msg, "org.kernel.kevent", "remove")) {
275                                 char **path = NULL;
276                                 if (!dbus_message_get_path_decomposed(msg, &path)) {
277                                         fprintf(stderr, "Can't get remove message path!\n");
278                                 } else {
279                                         unsigned int last;
280                                         for (last = 0; path[last] && last <= 6; last++);
281                                         if (last == 0 || last > 6) {
282                                                 fprintf(stderr, "Remove message has no path or path too long!\n");
283                                         } else {
284                                                 Device *device = device_search(devices, path[last - 1]);
285                                                 if (device) {
286                                                         removed(device, INETDEV);
287                                                 }
288                                         }
289                                         dbus_free_string_array(path);
290                                 }
291                         }
292                         dbus_message_unref(msg);
293                 }
294         }
295
296         device_delete_all(devices);
297         dbus_connection_unref(conn);
298         return 0;
299 }