Added header guard to device.h, wrote typedef in a cleaner way
[mtetherd] / mtetherd.c
1 /*
2   mtetherd
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         "usb0",
37         "bnep0",
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 // 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;
59
60 // Run loop active flag
61 static int running;
62 // Number of active tethering connections
63 static unsigned int active;
64
65 static void siginthandler(int sig) {
66         if (running) {
67                 fprintf(stderr, "SIGINIT received, exiting\n");
68                 running = 0;
69         } else {
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));
73                 }
74         }
75 }
76
77 static int launch(const char *args[]) {
78         pid_t pid = fork();
79         if (pid == 0) {
80                 if (execv(args[0], (char **const) args) == -1) {
81                         fprintf(stderr, "Error launching external process %s: %s\n", args[0], strerror(errno));
82                         exit(1);
83                 }
84         } else if (pid == -1) {
85                 fprintf(stderr, "Can't fork external process %s: %s\n", args[0], strerror(errno));
86                 return -1;
87         } else {
88                 /*printf("Launching:");
89                 size_t a;
90                 for (a = 0; args[a]; a++) {
91                         printf(" %s", args[a]);
92                 }
93                 printf("\n");*/
94                 int status = 0;
95                 if (waitpid(pid, &status, WNOHANG) == -1) {
96                         fprintf(stderr, "Error waiting for child process completion: %s\n", strerror(errno));
97                         return -2;
98                 }
99                 size_t i;
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));
104                                         return -3;
105                                 }
106                         } else if (WIFSIGNALED(status)) {
107                                 fprintf(stderr, "Child process killed by signal: %d\n", WTERMSIG(status));
108                                 return -4;
109                         }
110                         sleep(1);
111                         if (waitpid(pid, &status, WNOHANG) == -1) {
112                                 fprintf(stderr, "Error waiting for child process completion: %s\n", strerror(errno));
113                                 return -2;
114                         }
115                 }
116                 if (i >= TERMINATE_WAIT) {
117                         fprintf(stderr, "Child process %s still running after %u seconds, ignoring.\n", args[0], TERMINATE_WAIT);
118                         return -5;
119                 }
120         }
121         return 0;
122 }
123
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));
129                 return;
130         }
131         char *range = NULL;
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));
134                 free(runfile);
135                 return;
136         }
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));
144                 free(runfile);
145                 free(range);
146                 return;
147         }
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));
151                 free(runfile);
152                 free(range);
153                 free(forwsysctl);
154                 return;
155         }
156
157
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);
163                                         if (ffd < 0) {
164                                                 fprintf(stderr, "Can't enable forwarding on PAN device %s: %s\n", device->name, strerror(errno));
165                                         } else {
166                                                 if (write(ffd, "1", 1) == -1) {
167                                                         fprintf(stderr, "Can't enable forwarding on PAN device %s: %s\n", device->name, strerror(errno));
168                                                 } else {
169                                                         int ifd = open(inetforwsysctl, O_WRONLY, 0666);
170                                                         if (ifd < 0) {
171                                                                 fprintf(stderr, "Can't enable forwarding on WAN device %s (path %s): %s\n", inetdev, inetforwsysctl, strerror(errno));
172                                                         } else {
173                                                                 if (write(ifd, "1", 1) == -1) {
174                                                                         fprintf(stderr, "Can't enable forwarding on WAN device %s: %s\n", inetdev, strerror(errno));
175                                                                 }
176                                                                 close(ifd);
177                                                         }
178                                                 }
179                                                 close(ffd);
180                                         }
181                                 }
182                         }
183                 }
184         }
185
186         free(range);
187         free(runfile);
188         free(forwsysctl);
189         free(inetforwsysctl);
190
191         active++;
192 }
193
194 static void removed(Device *device, const char *inetdev) {
195         printf("Got org.kernel.kevent.remove on %s\n", device->name);
196
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));
200                 return;
201         }
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 };
204
205         // Errors are ignored here: we just disable as much as we can.
206         launch(dnsmasq);
207         if (unlink(runfile) == -1) {
208                 fprintf(stderr, "Error removing PID file for device %s: %s\n", device->name, strerror(errno));
209         }
210         launch(iptables);
211
212         free(runfile);
213
214         if (active > 0) active--;
215         if (active == 0) {
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));
219                         return;
220                 }
221                 int fd = open(inetforwsysctl, O_WRONLY, 0666);
222                 if (fd < 0) {
223                         fprintf(stderr, "Can't enable forwarding on WAN device %s: %s\n", inetdev, strerror(errno));
224                 } else {
225                         if (write(fd, "0", 1) == -1) {
226                                 fprintf(stderr, "Can't enable forwarding on WAN device %s: %s\n", inetdev, strerror(errno));
227                         }
228                         close(fd);
229                 }
230                 free(inetforwsysctl);
231         }
232 }
233
234 int main(int argc, const char *argv[]) {
235         active = 0;
236
237         Device *devices = NULL;
238         Device *node = NULL;
239         size_t i;
240         for (i = 0; DEVICES[i]; i++) {
241                 Device *device = device_new(DEVICES[i]);
242                 if (device) {
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);
248                                 if (!devices) {
249                                         devices = node;
250                                 }
251                         } else {
252                                 device_delete(device);
253                         }
254                 }
255         }
256
257         if (!devices) {
258                 fprintf(stderr, "Warning, no devices configured. I will just sit here and wait for nicer weather.\n");
259         }
260
261         DBusError err;
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);
267         }
268         if (!conn) {
269                 return 1;
270         }
271
272         for (node = devices; node; node = node->next) {
273                 char *filter = NULL;
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));
276                 } else {
277                         dbus_bus_add_match(conn, filter, NULL);
278                         free(filter);
279                 }
280         }
281         dbus_connection_flush(conn);
282
283         running = 1;
284         signal(SIGINT, siginthandler);
285         while (running) {
286                 dbus_connection_read_write(conn, POLL_MAINLOOP);
287                 DBusMessage* msg = dbus_connection_pop_message(conn);
288                 if (msg) {
289                         if (dbus_message_is_signal(msg, "org.kernel.kevent", "add")) {
290                                 char **path = NULL;
291                                 if (!dbus_message_get_path_decomposed(msg, &path)) {
292                                         fprintf(stderr, "Can't get add message path!\n");
293                                 } else {
294                                         unsigned int last;
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");
298                                         } else {
299                                                 Device *device = device_search(devices, path[last - 1]);
300                                                 if (device) {
301                                                         added(device, INETDEV);
302                                                 }
303                                         }
304                                         dbus_free_string_array(path);
305                                 }
306                         } else if (dbus_message_is_signal(msg, "org.kernel.kevent", "remove")) {
307                                 char **path = NULL;
308                                 if (!dbus_message_get_path_decomposed(msg, &path)) {
309                                         fprintf(stderr, "Can't get remove message path!\n");
310                                 } else {
311                                         unsigned int last;
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");
315                                         } else {
316                                                 Device *device = device_search(devices, path[last - 1]);
317                                                 if (device) {
318                                                         removed(device, INETDEV);
319                                                 }
320                                         }
321                                         dbus_free_string_array(path);
322                                 }
323                         }
324                         dbus_message_unref(msg);
325                 }
326         }
327
328         device_delete_all(devices);
329         dbus_connection_unref(conn);
330         return 0;
331 }