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 <arpa/inet.h>
32 #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[] = {
57 static unsigned int active;
59 static void siginthandler(int sig) {
60 fprintf(stderr, "SIGINIT received, exiting\n");
64 static int launch(const char *args[]) {
67 if (execv(args[0], (char **const) args) == -1) {
68 fprintf(stderr, "Error launching external process %s: %s\n", args[0], strerror(errno));
71 } else if (pid == -1) {
72 fprintf(stderr, "Can't fork external process %s: %s\n", args[0], strerror(errno));
76 if (waitpid(pid, &status, 0) == -1) {
77 perror("Error waiting for child process completion");
80 if (WIFEXITED(status)) {
81 if (WEXITSTATUS(status) != 0) {
82 fprintf(stderr, "Child process returned error: %d\n", WEXITSTATUS(status));
85 } else if (WIFSIGNALED(status)) {
86 fprintf(stderr, "Child process killed by signal: %d\n", WTERMSIG(status));
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);
96 if (inet_ntop(AF_INET, &address, ascaddress, sizeof(ascaddress)) == NULL) {
97 fprintf(stderr, "Can't convert address for device %s\n", device);
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);
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);
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));
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));
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));
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));
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);
148 fprintf(stderr, "Can't enable forwarding on PAN device %s: %s\n", device, strerror(errno));
150 if (write(ffd, "1", 1) == -1) {
151 fprintf(stderr, "Can't enable forwarding on PAN device %s: %s\n", device, strerror(errno));
153 int ifd = open(inetforwsysctl, O_WRONLY, 0666);
155 fprintf(stderr, "Can't enable forwarding on WAN device %s: %s\n", inetdev, strerror(errno));
157 if (write(ifd, "1", 1) == -1) {
158 fprintf(stderr, "Can't enable forwarding on WAN device %s: %s\n", inetdev, strerror(errno));
173 free(inetforwsysctl);
178 static void removed(const char *device, const char *inetdev) {
179 printf("Got org.kernel.kevent.remove on %s\n", device);
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));
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 };
189 // Errors are ignored here: we just disable as much as we can.
195 if (active > 0) active--;
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));
202 int fd = open(inetforwsysctl, O_WRONLY, 0666);
204 fprintf(stderr, "Can't enable forwarding on WAN device %s: %s\n", inetdev, strerror(errno));
206 if (write(fd, "0", 1) == -1) {
207 fprintf(stderr, "Can't enable forwarding on WAN device %s: %s\n", inetdev, strerror(errno));
211 free(inetforwsysctl);
215 static int finddev(const char* device, const char *devices[], size_t size) {
217 for (i = 0; i < size; i++) {
218 if (strcmp(device, devices[i]) == 0) {
225 int main(int argc, const char *argv[]) {
228 signal(SIGINT, siginthandler);
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;
237 for (i = 0; i < sizedevices; i++) {
238 int err = inet_pton(AF_INET, ADDRESSES[i], &addresses[numdevices]);
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]);
244 int err = inet_pton(AF_INET, STARTADDRESSES[i], &startaddresses[numdevices]);
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]);
250 int err = inet_pton(AF_INET, ENDADDRESSES[i], &endaddresses[numdevices]);
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]);
256 devices[numdevices] = DEVICES[i];
263 if (numdevices == 0) {
264 fprintf(stderr, "Warning, no devices configured. I will just sit here and wait for nicer weather.\n");
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);
278 for (i = 0; i < numdevices; i++) {
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));
283 dbus_bus_add_match(conn, filter, NULL);
286 dbus_connection_flush(conn);
289 dbus_connection_read_write(conn, 500);
290 DBusMessage* msg = dbus_connection_pop_message(conn);
292 if (dbus_message_is_signal(msg, "org.kernel.kevent", "add")) {
294 if (!dbus_message_get_path_decomposed(msg, &path)) {
295 fprintf(stderr, "Can't get add message path!\n");
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");
302 int index = finddev(path[last - 1], devices, numdevices);
305 added(devices[index], INETDEV, addresses[index], startaddresses[index], endaddresses[index]);
308 dbus_free_string_array(path);
310 } else if (dbus_message_is_signal(msg, "org.kernel.kevent", "remove")) {
312 if (!dbus_message_get_path_decomposed(msg, &path)) {
313 fprintf(stderr, "Can't get remove message path!\n");
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");
320 int index = finddev(path[last - 1], devices, numdevices);
322 removed(devices[index], INETDEV);
325 dbus_free_string_array(path);
328 dbus_message_unref(msg);
332 dbus_connection_unref(conn);