1/*
2 * Copyright (C) 2009 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#include <stdio.h>
18#include <stdlib.h>
19#include <string.h>
20#include <errno.h>
21#include <sys/ioctl.h>
22#include <sys/types.h>
23#include <sys/socket.h>
24#include <netinet/in.h>
25#include <arpa/inet.h>
26#include <linux/route.h>
27
28#include <android/log.h>
29#include <cutils/properties.h>
30
31static inline struct in_addr *in_addr(struct sockaddr *sa)
32{
33    return &((struct sockaddr_in *)sa)->sin_addr;
34}
35
36int main(int argc, char **argv)
37{
38    struct rtentry route = {
39        .rt_dst     = {.sa_family = AF_INET},
40        .rt_genmask = {.sa_family = AF_INET},
41        .rt_gateway = {.sa_family = AF_INET},
42        .rt_flags   = RTF_UP | RTF_GATEWAY,
43    };
44    FILE *f;
45    int s;
46
47    errno = EINVAL;
48    if (argc > 5 && inet_aton(argv[5], in_addr(&route.rt_gateway)) &&
49        (f = fopen("/proc/net/route", "r")) != NULL &&
50        (s = socket(AF_INET, SOCK_DGRAM, 0)) != -1) {
51        uint32_t *address = &in_addr(&route.rt_dst)->s_addr;
52        uint32_t *netmask = &in_addr(&route.rt_genmask)->s_addr;
53        char device[64];
54
55        fscanf(f, "%*[^\n]\n");
56        while (fscanf(f, "%63s%X%*X%*X%*d%*u%*d%X%*d%*u%*u\n",
57                      device, address, netmask) == 3) {
58            if (strcmp(argv[1], device)) {
59                uint32_t bit = ntohl(*netmask);
60                bit = htonl(bit ^ (1 << 31 | bit >> 1));
61                if (bit) {
62                    *netmask |= bit;
63                    if (ioctl(s, SIOCADDRT, &route) == -1 && errno != EEXIST) {
64                        break;
65                    }
66                    *address ^= bit;
67                    if (ioctl(s, SIOCADDRT, &route) == -1 && errno != EEXIST) {
68                        break;
69                    }
70                    errno = 0;
71                }
72            }
73        }
74    }
75
76    if (!errno) {
77        char *dns = getenv("DNS1");
78        property_set("vpn.dns1", dns ? dns : "");
79        dns = getenv("DNS2");
80        property_set("vpn.dns2", dns ? dns : "");
81        property_set("vpn.status", "ok");
82        __android_log_print(ANDROID_LOG_INFO, "ip-up-vpn",
83                            "All traffic is now redirected to %s", argv[5]);
84    } else {
85        property_set("vpn.status", "error");
86        __android_log_write(ANDROID_LOG_ERROR, "ip-up-vpn", strerror(errno));
87    }
88    return errno;
89}
90