Added GPLv3 license text and header
[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 <arpa/inet.h>
32 #include <dbus/dbus.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 static int running;
57 static unsigned int active;
58
59 static void siginthandler(int sig) {
60         fprintf(stderr, "SIGINIT received, exiting\n");
61         running = 0;
62 }
63
64 static int launch(const char *args[]) {
65         pid_t pid = fork();
66         if (pid == 0) {
67                 if (execv(args[0], (char **const) args) == -1) {
68                         fprintf(stderr, "Error launching external process %s: %s\n", args[0], strerror(errno));
69                         exit(1);
70                 }
71         } else if (pid == -1) {
72                 fprintf(stderr, "Can't fork external process %s: %s\n", args[0], strerror(errno));
73                 return -1;
74         } else {
75                 int status = 0;
76                 if (waitpid(pid, &status, 0) == -1) {
77                         perror("Error waiting for child process completion");
78                         return -1;
79                 }
80                 if (WIFEXITED(status)) {
81                         if (WEXITSTATUS(status) != 0) {
82                                 fprintf(stderr, "Child process returned error: %d\n", WEXITSTATUS(status));
83                                 return -1;
84                         }
85                 } else if (WIFSIGNALED(status)) {
86                         fprintf(stderr, "Child process killed by signal: %d\n", WTERMSIG(status));
87                         return -1;
88                 }
89         }
90         return 0;
91 }
92
93 static void added(const char *device, const char *inetdev, struct in_addr address, struct in_addr startaddress, struct in_addr endaddress) {
94         printf("Got org.kernel.kevent.add on %s\n", device);
95         char ascaddress[16];
96         if (inet_ntop(AF_INET, &address, ascaddress, sizeof(ascaddress)) == NULL) {
97                 fprintf(stderr, "Can't convert address for device %s\n", device);
98                 return;
99         }
100         char ascstartaddress[16];
101         if (inet_ntop(AF_INET, &startaddress, ascstartaddress, sizeof(ascstartaddress)) == NULL) {
102                 fprintf(stderr, "Can't convert start address for device %s\n", device);
103                 return;
104         }
105         char ascendaddress[16];
106         if (inet_ntop(AF_INET, &endaddress, ascendaddress, sizeof(ascendaddress)) == NULL) {
107                 fprintf(stderr, "Can't convert end address for device %s\n", device);
108                 return;
109         }
110         char *runfile = NULL;
111         if (asprintf(&runfile, "/var/run/tethering.%s.pid", device) == -1) {
112                 fprintf(stderr, "Can't construct PID file name for device %s: %s\n", device, strerror(errno));
113                 return;
114         }
115         char *range = NULL;
116         if (asprintf(&range, "%s-%s,3600", ascstartaddress, ascendaddress) == -1) {
117                 fprintf(stderr, "Can't construct address range parameter for device %s: %s\n", device, strerror(errno));
118                 free(runfile);
119                 return;
120         }
121         const char *ifconfig[] = { "/sbin/ifconfig", device, ascaddress, "up", NULL };
122         const char *modprobe[] = { "/sbin/modprobe", "ipt_MASQUERADE", NULL };
123         const char *iptables[] = { "/usr/sbin/iptables", "-t", "nat", "-A", "POSTROUTING", "-o", inetdev, "-j", "MASQUERADE", NULL };
124         const char *dnsmasq[] = { "/sbin/start-stop-daemon", "-S", "-p", runfile, "-m", "-b", "-x", "/usr/sbin/dnsmasq", "--", "-k", "-I", "lo", "-i", device, "-a", ascaddress, "-z", "-F", range, NULL };
125         char *forwsysctl = NULL;
126         if (asprintf(&forwsysctl, "/proc/sys/net/ipv4/conf/%s/forwarding", device) == -1) {
127                 fprintf(stderr, "Can't construct sysctl path for device %s: %s\n", device, strerror(errno));
128                 free(runfile);
129                 free(range);
130                 return;
131         }
132         char *inetforwsysctl = NULL;
133         if (asprintf(&inetforwsysctl, "/proc/sys/net/ipv4/conf/%s/forwarding", inetdev) == -1) {
134                 fprintf(stderr, "Can't construct inet sysctl path for device %s: %s\n", device, strerror(errno));
135                 free(runfile);
136                 free(range);
137                 free(forwsysctl);
138                 return;
139         }
140
141
142         if (launch(ifconfig) == 0) {
143                 if (launch(modprobe) == 0) {
144                         if (launch(iptables) == 0) {
145                                 if (launch(dnsmasq) == 0) {
146                                         int ffd = open(forwsysctl, O_WRONLY, 0666);
147                                         if (ffd < 0) {
148                                                 fprintf(stderr, "Can't enable forwarding on PAN device %s: %s\n", device, strerror(errno));
149                                         } else {
150                                                 if (write(ffd, "1", 1) == -1) {
151                                                         fprintf(stderr, "Can't enable forwarding on PAN device %s: %s\n", device, strerror(errno));
152                                                 } else {
153                                                         int ifd = open(inetforwsysctl, O_WRONLY, 0666);
154                                                         if (ifd < 0) {
155                                                                 fprintf(stderr, "Can't enable forwarding on WAN device %s: %s\n", inetdev, strerror(errno));
156                                                         } else {
157                                                                 if (write(ifd, "1", 1) == -1) {
158                                                                         fprintf(stderr, "Can't enable forwarding on WAN device %s: %s\n", inetdev, strerror(errno));
159                                                                 }
160                                                                 close(ifd);
161                                                         }
162                                                 }
163                                                 close(ffd);
164                                         }
165                                 }
166                         }
167                 }
168         }
169
170         free(range);
171         free(runfile);
172         free(forwsysctl);
173         free(inetforwsysctl);
174
175         active++;
176 }
177
178 static void removed(const char *device, const char *inetdev) {
179         printf("Got org.kernel.kevent.remove on %s\n", device);
180
181         char *runfile = NULL;
182         if (asprintf(&runfile, "/var/run/tethering.%s.pid", device) == -1) {
183                 fprintf(stderr, "Can't construct PID file name for device %s: %s\n", device, strerror(errno));
184                 return;
185         }
186         const char *dnsmasq[] = { "/sbin/start-stop-daemon", "-K", "-p", runfile, "-x", "/usr/sbin/dnsmasq", NULL };
187         const char *iptables[] = { "/usr/sbin/iptables", "-t", "nat", "-D", "POSTROUTING", "-o", inetdev, "-j", "MASQUERADE", NULL };
188
189         // Errors are ignored here: we just disable as much as we can.
190         launch(dnsmasq);
191         launch(iptables);
192
193         free(runfile);
194
195         if (active > 0) active--;
196         if (active == 0) {
197                 char *inetforwsysctl = NULL;
198                 if (asprintf(&inetforwsysctl, "/proc/sys/net/ipv4/conf/%s/forwarding", inetdev) == -1) {
199                         fprintf(stderr, "Can't construct inet sysctl path for device %s: %s\n", device, strerror(errno));
200                         return;
201                 }
202                 int fd = open(inetforwsysctl, O_WRONLY, 0666);
203                 if (fd < 0) {
204                         fprintf(stderr, "Can't enable forwarding on WAN device %s: %s\n", inetdev, strerror(errno));
205                 } else {
206                         if (write(fd, "0", 1) == -1) {
207                                 fprintf(stderr, "Can't enable forwarding on WAN device %s: %s\n", inetdev, strerror(errno));
208                         }
209                         close(fd);
210                 }
211                 free(inetforwsysctl);
212         }
213 }
214
215 static int finddev(const char* device, const char *devices[], size_t size) {
216         int i;
217         for (i = 0; i < size; i++) {
218                 if (strcmp(device, devices[i]) == 0) {
219                         return i;
220                 }
221         }
222         return -1;
223 }
224
225 int main(int argc, const char *argv[]) {
226         running = 1;
227         active = 0;
228         signal(SIGINT, siginthandler);
229
230         size_t sizedevices = sizeof(DEVICES) / sizeof(DEVICES[0]);
231         const char *devices[sizedevices];
232         struct in_addr addresses[sizedevices];
233         struct in_addr startaddresses[sizedevices];
234         struct in_addr endaddresses[sizedevices];
235         size_t numdevices = 0;
236         size_t i;
237         for (i = 0; i < sizedevices; i++) {
238                 int err = inet_pton(AF_INET, ADDRESSES[i], &addresses[numdevices]);
239                 if (err == 0) {
240                         fprintf(stderr, "Invalid address for device %s: %s\n", DEVICES[i], ADDRESSES[i]);
241                 } else if (err == -1) {
242                         fprintf(stderr, "Error converting address for device %s: %s\n", DEVICES[i], ADDRESSES[i]);
243                 } else {
244                         int err = inet_pton(AF_INET, STARTADDRESSES[i], &startaddresses[numdevices]);
245                         if (err == 0) {
246                                 fprintf(stderr, "Invalid DHCP start address for device %s: %s\n", DEVICES[i], STARTADDRESSES[i]);
247                         } else if (err == -1) {
248                                 fprintf(stderr, "Error converting DHCP start address for device %s: %s\n", DEVICES[i], STARTADDRESSES[i]);
249                         } else {
250                                 int err = inet_pton(AF_INET, ENDADDRESSES[i], &endaddresses[numdevices]);
251                                 if (err == 0) {
252                                         fprintf(stderr, "Invalid DHCP end address for device %s: %s\n", DEVICES[i], ENDADDRESSES[i]);
253                                 } else if (err == -1) {
254                                         fprintf(stderr, "Error converting DHCP end address for device %s: %s\n", DEVICES[i], ENDADDRESSES[i]);
255                                 } else {
256                                         devices[numdevices] = DEVICES[i];
257                                         numdevices++;
258                                 }
259                         }
260                 }
261         }
262         
263         if (numdevices == 0) {
264                 fprintf(stderr, "Warning, no devices configured. I will just sit here and wait for nicer weather.\n");
265         }
266
267         DBusError err;
268         dbus_error_init(&err);
269         DBusConnection *conn = dbus_bus_get(DBUS_BUS_SYSTEM, &err);
270         if (dbus_error_is_set(&err)) {
271                 fprintf(stderr, "Error %s occured: %s\n", err.name, err.message);
272                 dbus_error_free(&err);
273         }
274         if (!conn) {
275                 return 1;
276         }
277
278         for (i = 0; i < numdevices; i++) {
279                 char *filter = NULL;
280                 if (!asprintf(&filter, "type='signal',interface='org.kernel.kevent',path='/org/kernel/class/net/%s'", devices[i])) {
281                         fprintf(stderr, "Can't construct filter rule for device %s: %s\n", devices[i], strerror(errno));
282                 } else {
283                         dbus_bus_add_match(conn, filter, NULL);
284                 }
285         }
286         dbus_connection_flush(conn);
287
288         while (running) {
289                 dbus_connection_read_write(conn, 500);
290                 DBusMessage* msg = dbus_connection_pop_message(conn);
291                 if (msg) {
292                         if (dbus_message_is_signal(msg, "org.kernel.kevent", "add")) {
293                                 char **path = NULL;
294                                 if (!dbus_message_get_path_decomposed(msg, &path)) {
295                                         fprintf(stderr, "Can't get add message path!\n");
296                                 } else {
297                                         unsigned int last;
298                                         for (last = 0; path[last] && last <= 6; last++);
299                                         if (last == 0 || last > 6) {
300                                                 fprintf(stderr, "Add message has no path or path too long!\n");
301                                         } else {
302                                                 int index = finddev(path[last - 1], devices, numdevices);
303                                                 if (index >= 0) {
304                                                         
305                                                         added(devices[index], INETDEV, addresses[index], startaddresses[index], endaddresses[index]);
306                                                 }
307                                         }
308                                         dbus_free_string_array(path);
309                                 }
310                         } else if (dbus_message_is_signal(msg, "org.kernel.kevent", "remove")) {
311                                 char **path = NULL;
312                                 if (!dbus_message_get_path_decomposed(msg, &path)) {
313                                         fprintf(stderr, "Can't get remove message path!\n");
314                                 } else {
315                                         unsigned int last;
316                                         for (last = 0; path[last] && last <= 6; last++);
317                                         if (last == 0 || last > 6) {
318                                                 fprintf(stderr, "Remove message has no path or path too long!\n");
319                                         } else {
320                                                 int index = finddev(path[last - 1], devices, numdevices);
321                                                 if (index >= 0) {
322                                                         removed(devices[index], INETDEV);
323                                                 }
324                                         }
325                                         dbus_free_string_array(path);
326                                 }
327                         }
328                         dbus_message_unref(msg);
329                 }
330         }
331
332         dbus_connection_unref(conn);
333         return 0;
334 }