ifc_utils.c revision 8984bb9691f8d3e2665f7aae0896b9bd2ade0c19
1/*
2 * Copyright 2008, 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 <unistd.h>
20#include <string.h>
21#include <errno.h>
22
23#include <sys/socket.h>
24#include <sys/select.h>
25#include <sys/types.h>
26#include <netinet/in.h>
27#include <arpa/inet.h>
28
29#include <linux/if.h>
30#include <linux/if_ether.h>
31#include <linux/if_arp.h>
32#include <linux/sockios.h>
33#include <linux/route.h>
34#include <linux/ipv6_route.h>
35#include <netdb.h>
36#include <net/if.h>
37#include <linux/wireless.h>
38
39#ifdef ANDROID
40#define LOG_TAG "NetUtils"
41#include <cutils/log.h>
42#include <cutils/properties.h>
43#else
44#include <stdio.h>
45#include <string.h>
46#define LOGD printf
47#define LOGW printf
48#endif
49
50static int ifc_ctl_sock = -1;
51static int ifc_ctl_sock6 = -1;
52void printerr(char *fmt, ...);
53
54static const char *ipaddr_to_string(in_addr_t addr)
55{
56    struct in_addr in_addr;
57
58    in_addr.s_addr = addr;
59    return inet_ntoa(in_addr);
60}
61
62int ifc_init(void)
63{
64    if (ifc_ctl_sock == -1) {
65        ifc_ctl_sock = socket(AF_INET, SOCK_DGRAM, 0);
66        if (ifc_ctl_sock < 0) {
67            printerr("socket() failed: %s\n", strerror(errno));
68        }
69    }
70    return ifc_ctl_sock < 0 ? -1 : 0;
71}
72
73int ifc_init6(void)
74{
75    if (ifc_ctl_sock6 == -1) {
76        ifc_ctl_sock6 = socket(AF_INET6, SOCK_DGRAM, 0);
77        if (ifc_ctl_sock6 < 0) {
78            printerr("socket() failed: %s\n", strerror(errno));
79        }
80    }
81    return ifc_ctl_sock6 < 0 ? -1 : 0;
82}
83
84void ifc_close(void)
85{
86    if (ifc_ctl_sock != -1) {
87        (void)close(ifc_ctl_sock);
88        ifc_ctl_sock = -1;
89    }
90}
91
92void ifc_close6(void)
93{
94    if (ifc_ctl_sock6 != -1) {
95        (void)close(ifc_ctl_sock6);
96        ifc_ctl_sock6 = -1;
97    }
98}
99
100static void ifc_init_ifr(const char *name, struct ifreq *ifr)
101{
102    memset(ifr, 0, sizeof(struct ifreq));
103    strncpy(ifr->ifr_name, name, IFNAMSIZ);
104    ifr->ifr_name[IFNAMSIZ - 1] = 0;
105}
106
107int ifc_get_hwaddr(const char *name, void *ptr)
108{
109    int r;
110    struct ifreq ifr;
111    ifc_init_ifr(name, &ifr);
112
113    r = ioctl(ifc_ctl_sock, SIOCGIFHWADDR, &ifr);
114    if(r < 0) return -1;
115
116    memcpy(ptr, &ifr.ifr_hwaddr.sa_data, ETH_ALEN);
117    return 0;
118}
119
120int ifc_get_ifindex(const char *name, int *if_indexp)
121{
122    int r;
123    struct ifreq ifr;
124    ifc_init_ifr(name, &ifr);
125
126    r = ioctl(ifc_ctl_sock, SIOCGIFINDEX, &ifr);
127    if(r < 0) return -1;
128
129    *if_indexp = ifr.ifr_ifindex;
130    return 0;
131}
132
133static int ifc_set_flags(const char *name, unsigned set, unsigned clr)
134{
135    struct ifreq ifr;
136    ifc_init_ifr(name, &ifr);
137
138    if(ioctl(ifc_ctl_sock, SIOCGIFFLAGS, &ifr) < 0) return -1;
139    ifr.ifr_flags = (ifr.ifr_flags & (~clr)) | set;
140    return ioctl(ifc_ctl_sock, SIOCSIFFLAGS, &ifr);
141}
142
143int ifc_up(const char *name)
144{
145    return ifc_set_flags(name, IFF_UP, 0);
146}
147
148int ifc_down(const char *name)
149{
150    return ifc_set_flags(name, 0, IFF_UP);
151}
152
153static void init_sockaddr_in(struct sockaddr *sa, in_addr_t addr)
154{
155    struct sockaddr_in *sin = (struct sockaddr_in *) sa;
156    sin->sin_family = AF_INET;
157    sin->sin_port = 0;
158    sin->sin_addr.s_addr = addr;
159}
160
161int ifc_set_addr(const char *name, in_addr_t addr)
162{
163    struct ifreq ifr;
164
165    ifc_init_ifr(name, &ifr);
166    init_sockaddr_in(&ifr.ifr_addr, addr);
167
168    return ioctl(ifc_ctl_sock, SIOCSIFADDR, &ifr);
169}
170
171int ifc_set_hwaddr(const char *name, const void *ptr)
172{
173    int r;
174    struct ifreq ifr;
175    ifc_init_ifr(name, &ifr);
176
177    ifr.ifr_hwaddr.sa_family = ARPHRD_ETHER;
178    memcpy(&ifr.ifr_hwaddr.sa_data, ptr, ETH_ALEN);
179    return ioctl(ifc_ctl_sock, SIOCSIFHWADDR, &ifr);
180}
181
182int ifc_set_mask(const char *name, in_addr_t mask)
183{
184    struct ifreq ifr;
185
186    ifc_init_ifr(name, &ifr);
187    init_sockaddr_in(&ifr.ifr_addr, mask);
188
189    return ioctl(ifc_ctl_sock, SIOCSIFNETMASK, &ifr);
190}
191
192int ifc_get_info(const char *name, in_addr_t *addr, in_addr_t *mask, unsigned *flags)
193{
194    struct ifreq ifr;
195    ifc_init_ifr(name, &ifr);
196
197    if (addr != NULL) {
198        if(ioctl(ifc_ctl_sock, SIOCGIFADDR, &ifr) < 0) {
199            *addr = 0;
200        } else {
201            *addr = ((struct sockaddr_in*) &ifr.ifr_addr)->sin_addr.s_addr;
202        }
203    }
204
205    if (mask != NULL) {
206        if(ioctl(ifc_ctl_sock, SIOCGIFNETMASK, &ifr) < 0) {
207            *mask = 0;
208        } else {
209            *mask = ((struct sockaddr_in*) &ifr.ifr_addr)->sin_addr.s_addr;
210        }
211    }
212
213    if (flags != NULL) {
214        if(ioctl(ifc_ctl_sock, SIOCGIFFLAGS, &ifr) < 0) {
215            *flags = 0;
216        } else {
217            *flags = ifr.ifr_flags;
218        }
219    }
220
221    return 0;
222}
223
224in_addr_t get_ipv4_netmask(int prefix_length)
225{
226    in_addr_t mask = 0;
227
228    mask = ~mask << (32 - prefix_length);
229    mask = htonl(mask);
230
231    return mask;
232}
233
234int ifc_add_ipv4_route(const char *ifname, struct in_addr dst, int prefix_length,
235      struct in_addr gw)
236{
237    struct rtentry rt;
238    int result;
239    in_addr_t netmask;
240
241    memset(&rt, 0, sizeof(rt));
242
243    rt.rt_dst.sa_family = AF_INET;
244    rt.rt_dev = (void*) ifname;
245
246    netmask = get_ipv4_netmask(prefix_length);
247    init_sockaddr_in(&rt.rt_genmask, netmask);
248    init_sockaddr_in(&rt.rt_dst, dst.s_addr);
249    rt.rt_flags = RTF_UP;
250
251    if (prefix_length == 32) {
252        rt.rt_flags |= RTF_HOST;
253    }
254
255    if (gw.s_addr != 0) {
256        rt.rt_flags |= RTF_GATEWAY;
257        init_sockaddr_in(&rt.rt_gateway, gw.s_addr);
258    }
259
260    ifc_init();
261
262    if (ifc_ctl_sock < 0) {
263        return -errno;
264    }
265
266    result = ioctl(ifc_ctl_sock, SIOCADDRT, &rt);
267    if (result < 0) {
268        if (errno == EEXIST) {
269            result = 0;
270        } else {
271            result = -errno;
272        }
273    }
274    ifc_close();
275    return result;
276}
277
278int ifc_create_default_route(const char *name, in_addr_t gw)
279{
280    struct in_addr in_dst, in_gw;
281
282    in_dst.s_addr = 0;
283    in_gw.s_addr = gw;
284
285    return ifc_add_ipv4_route(name, in_dst, 0, in_gw);
286}
287
288int ifc_add_host_route(const char *name, in_addr_t dst)
289{
290    struct in_addr in_dst, in_gw;
291
292    in_dst.s_addr = dst;
293    in_gw.s_addr = 0;
294
295    return ifc_add_ipv4_route(name, in_dst, 32, in_gw);
296}
297
298int ifc_enable(const char *ifname)
299{
300    int result;
301
302    ifc_init();
303    result = ifc_up(ifname);
304    ifc_close();
305    return result;
306}
307
308int ifc_disable(const char *ifname)
309{
310    int result;
311
312    ifc_init();
313    result = ifc_down(ifname);
314    ifc_set_addr(ifname, 0);
315    ifc_close();
316    return result;
317}
318
319int ifc_reset_connections(const char *ifname)
320{
321#ifdef HAVE_ANDROID_OS
322    int result;
323    in_addr_t myaddr;
324    struct ifreq ifr;
325
326    ifc_init();
327    ifc_get_info(ifname, &myaddr, NULL, NULL);
328    ifc_init_ifr(ifname, &ifr);
329    init_sockaddr_in(&ifr.ifr_addr, myaddr);
330    result = ioctl(ifc_ctl_sock, SIOCKILLADDR,  &ifr);
331    ifc_close();
332
333    return result;
334#else
335    return 0;
336#endif
337}
338
339/*
340 * Remove the routes associated with the named interface.
341 */
342int ifc_remove_host_routes(const char *name)
343{
344    char ifname[64];
345    in_addr_t dest, gway, mask;
346    int flags, refcnt, use, metric, mtu, win, irtt;
347    struct rtentry rt;
348    FILE *fp;
349    struct in_addr addr;
350
351    fp = fopen("/proc/net/route", "r");
352    if (fp == NULL)
353        return -1;
354    /* Skip the header line */
355    if (fscanf(fp, "%*[^\n]\n") < 0) {
356        fclose(fp);
357        return -1;
358    }
359    ifc_init();
360    for (;;) {
361        int nread = fscanf(fp, "%63s%X%X%X%d%d%d%X%d%d%d\n",
362                           ifname, &dest, &gway, &flags, &refcnt, &use, &metric, &mask,
363                           &mtu, &win, &irtt);
364        if (nread != 11) {
365            break;
366        }
367        if ((flags & (RTF_UP|RTF_HOST)) != (RTF_UP|RTF_HOST)
368                || strcmp(ifname, name) != 0) {
369            continue;
370        }
371        memset(&rt, 0, sizeof(rt));
372        rt.rt_dev = (void *)name;
373        init_sockaddr_in(&rt.rt_dst, dest);
374        init_sockaddr_in(&rt.rt_gateway, gway);
375        init_sockaddr_in(&rt.rt_genmask, mask);
376        addr.s_addr = dest;
377        if (ioctl(ifc_ctl_sock, SIOCDELRT, &rt) < 0) {
378            LOGD("failed to remove route for %s to %s: %s",
379                 ifname, inet_ntoa(addr), strerror(errno));
380        }
381    }
382    fclose(fp);
383    ifc_close();
384    return 0;
385}
386
387/*
388 * Return the address of the default gateway
389 *
390 * TODO: factor out common code from this and remove_host_routes()
391 * so that we only scan /proc/net/route in one place.
392 */
393int ifc_get_default_route(const char *ifname)
394{
395    char name[64];
396    in_addr_t dest, gway, mask;
397    int flags, refcnt, use, metric, mtu, win, irtt;
398    int result;
399    FILE *fp;
400
401    fp = fopen("/proc/net/route", "r");
402    if (fp == NULL)
403        return 0;
404    /* Skip the header line */
405    if (fscanf(fp, "%*[^\n]\n") < 0) {
406        fclose(fp);
407        return 0;
408    }
409    ifc_init();
410    result = 0;
411    for (;;) {
412        int nread = fscanf(fp, "%63s%X%X%X%d%d%d%X%d%d%d\n",
413                           name, &dest, &gway, &flags, &refcnt, &use, &metric, &mask,
414                           &mtu, &win, &irtt);
415        if (nread != 11) {
416            break;
417        }
418        if ((flags & (RTF_UP|RTF_GATEWAY)) == (RTF_UP|RTF_GATEWAY)
419                && dest == 0
420                && strcmp(ifname, name) == 0) {
421            result = gway;
422            break;
423        }
424    }
425    fclose(fp);
426    ifc_close();
427    return result;
428}
429
430/*
431 * Sets the specified gateway as the default route for the named interface.
432 */
433int ifc_set_default_route(const char *ifname, in_addr_t gateway)
434{
435    struct in_addr addr;
436    int result;
437
438    ifc_init();
439    addr.s_addr = gateway;
440    if ((result = ifc_create_default_route(ifname, gateway)) < 0) {
441        LOGD("failed to add %s as default route for %s: %s",
442             inet_ntoa(addr), ifname, strerror(errno));
443    }
444    ifc_close();
445    return result;
446}
447
448/*
449 * Removes the default route for the named interface.
450 */
451int ifc_remove_default_route(const char *ifname)
452{
453    struct rtentry rt;
454    int result;
455
456    ifc_init();
457    memset(&rt, 0, sizeof(rt));
458    rt.rt_dev = (void *)ifname;
459    rt.rt_flags = RTF_UP|RTF_GATEWAY;
460    init_sockaddr_in(&rt.rt_dst, 0);
461    if ((result = ioctl(ifc_ctl_sock, SIOCDELRT, &rt)) < 0) {
462        LOGD("failed to remove default route for %s: %s", ifname, strerror(errno));
463    }
464    ifc_close();
465    return result;
466}
467
468int
469ifc_configure(const char *ifname,
470        in_addr_t address,
471        in_addr_t netmask,
472        in_addr_t gateway,
473        in_addr_t dns1,
474        in_addr_t dns2) {
475
476    char dns_prop_name[PROPERTY_KEY_MAX];
477
478    ifc_init();
479
480    if (ifc_up(ifname)) {
481        printerr("failed to turn on interface %s: %s\n", ifname, strerror(errno));
482        ifc_close();
483        return -1;
484    }
485    if (ifc_set_addr(ifname, address)) {
486        printerr("failed to set ipaddr %s: %s\n", ipaddr_to_string(address), strerror(errno));
487        ifc_close();
488        return -1;
489    }
490    if (ifc_set_mask(ifname, netmask)) {
491        printerr("failed to set netmask %s: %s\n", ipaddr_to_string(netmask), strerror(errno));
492        ifc_close();
493        return -1;
494    }
495    if (ifc_create_default_route(ifname, gateway)) {
496        printerr("failed to set default route %s: %s\n", ipaddr_to_string(gateway), strerror(errno));
497        ifc_close();
498        return -1;
499    }
500
501    ifc_close();
502
503    snprintf(dns_prop_name, sizeof(dns_prop_name), "net.%s.dns1", ifname);
504    property_set(dns_prop_name, dns1 ? ipaddr_to_string(dns1) : "");
505    snprintf(dns_prop_name, sizeof(dns_prop_name), "net.%s.dns2", ifname);
506    property_set(dns_prop_name, dns2 ? ipaddr_to_string(dns2) : "");
507
508    return 0;
509}
510
511int ifc_add_ipv6_route(const char *ifname, struct in6_addr dst, int prefix_length,
512      struct in6_addr gw)
513{
514    struct in6_rtmsg rtmsg;
515    int result;
516    int ifindex;
517
518    memset(&rtmsg, 0, sizeof(rtmsg));
519
520    ifindex = if_nametoindex(ifname);
521    if (ifindex == 0) {
522        printerr("if_nametoindex() failed: interface %s\n", ifname);
523        return -ENXIO;
524    }
525
526    rtmsg.rtmsg_ifindex = ifindex;
527    rtmsg.rtmsg_dst = dst;
528    rtmsg.rtmsg_dst_len = prefix_length;
529    rtmsg.rtmsg_flags = RTF_UP;
530
531    if (prefix_length == 128) {
532        rtmsg.rtmsg_flags |= RTF_HOST;
533    }
534
535    if (memcmp(&gw, &in6addr_any, sizeof(in6addr_any))) {
536        rtmsg.rtmsg_flags |= RTF_GATEWAY;
537        rtmsg.rtmsg_gateway = gw;
538    }
539
540    ifc_init6();
541
542    if (ifc_ctl_sock6 < 0) {
543        return -errno;
544    }
545
546    result = ioctl(ifc_ctl_sock6, SIOCADDRT, &rtmsg);
547    if (result < 0) {
548        if (errno == EEXIST) {
549            result = 0;
550        } else {
551            result = -errno;
552        }
553    }
554    ifc_close6();
555    return result;
556}
557
558int ifc_add_route(const char *ifname, const char *dst, int prefix_length,
559      const char *gw)
560{
561    int ret = 0;
562    struct sockaddr_in ipv4_dst, ipv4_gw;
563    struct sockaddr_in6 ipv6_dst, ipv6_gw;
564    struct addrinfo hints, *addr_ai, *gw_ai;
565
566    memset(&hints, 0, sizeof(hints));
567    hints.ai_family = AF_UNSPEC;  /* Allow IPv4 or IPv6 */
568    hints.ai_flags = AI_NUMERICHOST;
569
570    ret = getaddrinfo(dst, NULL, &hints, &addr_ai);
571
572    if (ret != 0) {
573        printerr("getaddrinfo failed: invalid address %s\n", dst);
574        return -EINVAL;
575    }
576
577    if (gw == NULL) {
578        if (addr_ai->ai_family == AF_INET6) {
579            gw = "::";
580        } else if (addr_ai->ai_family == AF_INET) {
581            gw = "0.0.0.0";
582        }
583    }
584
585    ret = getaddrinfo(gw, NULL, &hints, &gw_ai);
586    if (ret != 0) {
587        printerr("getaddrinfo failed: invalid gateway %s\n", gw);
588        freeaddrinfo(addr_ai);
589        return -EINVAL;
590    }
591
592    if (addr_ai->ai_family != gw_ai->ai_family) {
593        printerr("ifc_add_route: different address families: %s and %s\n", dst, gw);
594        freeaddrinfo(addr_ai);
595        freeaddrinfo(gw_ai);
596        return -EINVAL;
597    }
598
599    if (addr_ai->ai_family == AF_INET6) {
600        memcpy(&ipv6_dst, addr_ai->ai_addr, sizeof(struct sockaddr_in6));
601        memcpy(&ipv6_gw, gw_ai->ai_addr, sizeof(struct sockaddr_in6));
602        ret = ifc_add_ipv6_route(ifname, ipv6_dst.sin6_addr, prefix_length,
603              ipv6_gw.sin6_addr);
604    } else if (addr_ai->ai_family == AF_INET) {
605        memcpy(&ipv4_dst, addr_ai->ai_addr, sizeof(struct sockaddr_in));
606        memcpy(&ipv4_gw, gw_ai->ai_addr, sizeof(struct sockaddr_in));
607        ret = ifc_add_ipv4_route(ifname, ipv4_dst.sin_addr, prefix_length,
608              ipv4_gw.sin_addr);
609    } else {
610        printerr("ifc_add_route: getaddrinfo returned un supported address family %d\n",
611                  addr_ai->ai_family);
612        ret = -EAFNOSUPPORT;
613    }
614
615    freeaddrinfo(addr_ai);
616    freeaddrinfo(gw_ai);
617    return ret;
618}
619