1/* external/dhcpcd/ifaddrs.c
2**
3** Copyright 2011, The Android Open Source Project
4**
5** Licensed under the Apache License, Version 2.0 (the "License");.
6** you may not use this file except in compliance with the License..
7** You may obtain a copy of the License at.
8**
9**     http://www.apache.org/licenses/LICENSE-2.0.
10**
11** Unless required by applicable law or agreed to in writing, software.
12** distributed under the License is distributed on an "AS IS" BASIS,.
13** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied..
14** See the License for the specific language governing permissions and.
15** limitations under the License.
16*/
17
18#include <arpa/inet.h>
19#include <sys/socket.h>
20#include "ifaddrs.h"
21#include <stdio.h>
22#include <stdlib.h>
23#include <unistd.h>
24#include <sys/types.h>
25#include <dirent.h>
26#include <netinet/ether.h>
27#include <netdb.h>
28#include <linux/if_packet.h>
29#include <netinet/if_ether.h>
30#include <linux/if_arp.h>
31#include <netutils/ifc.h>
32
33struct ifaddrs *get_interface(const char *name, sa_family_t family)
34{
35    unsigned addr, flags;
36    int masklen;
37    struct ifaddrs *ifa;
38    struct sockaddr_in *saddr = NULL;
39    struct sockaddr_in *smask = NULL;
40    struct sockaddr_ll *hwaddr = NULL;
41    unsigned char hwbuf[ETH_ALEN];
42
43    if (ifc_get_info(name, &addr, &masklen, &flags))
44        return NULL;
45
46    if ((family == AF_INET) && (addr == 0))
47        return NULL;
48
49    ifa = malloc(sizeof(struct ifaddrs));
50    if (!ifa)
51        return NULL;
52    memset(ifa, 0, sizeof(struct ifaddrs));
53
54    ifa->ifa_name = malloc(strlen(name)+1);
55    if (!ifa->ifa_name) {
56        free(ifa);
57        return NULL;
58    }
59    strcpy(ifa->ifa_name, name);
60    ifa->ifa_flags = flags;
61
62    if (family == AF_INET) {
63        saddr = malloc(sizeof(struct sockaddr_in));
64        if (saddr) {
65            saddr->sin_addr.s_addr = addr;
66            saddr->sin_family = family;
67        }
68        ifa->ifa_addr = (struct sockaddr *)saddr;
69
70        if (masklen != 0) {
71            smask = malloc(sizeof(struct sockaddr_in));
72            if (smask) {
73                smask->sin_addr.s_addr = prefixLengthToIpv4Netmask(masklen);
74                smask->sin_family = family;
75            }
76        }
77        ifa->ifa_netmask = (struct sockaddr *)smask;
78    } else if (family == AF_PACKET) {
79        if (!ifc_get_hwaddr(name, hwbuf)) {
80            hwaddr = malloc(sizeof(struct sockaddr_ll));
81            if (hwaddr) {
82                memset(hwaddr, 0, sizeof(struct sockaddr_ll));
83                hwaddr->sll_family = family;
84                /* hwaddr->sll_protocol = ETHERTYPE_IP; */
85                hwaddr->sll_hatype = ARPHRD_ETHER;
86                hwaddr->sll_halen = ETH_ALEN;
87                memcpy(hwaddr->sll_addr, hwbuf, ETH_ALEN);
88            }
89        }
90        ifa->ifa_addr = (struct sockaddr *)hwaddr;
91        ifa->ifa_netmask = (struct sockaddr *)smask;
92    }
93    return ifa;
94}
95
96int getifaddrs(struct ifaddrs **ifap)
97{
98    DIR *d;
99    struct dirent *de;
100    struct ifaddrs *ifa;
101    struct ifaddrs *ifah = NULL;
102
103    if (!ifap)
104        return -1;
105    *ifap = NULL;
106
107    if (ifc_init())
108       return -1;
109
110    d = opendir("/sys/class/net");
111    if (d == 0)
112        return -1;
113    while ((de = readdir(d))) {
114        if (de->d_name[0] == '.')
115            continue;
116        ifa = get_interface(de->d_name, AF_INET);
117        if (ifa != NULL) {
118            ifa->ifa_next = ifah;
119            ifah = ifa;
120        }
121        ifa = get_interface(de->d_name, AF_PACKET);
122        if (ifa != NULL) {
123            ifa->ifa_next = ifah;
124            ifah = ifa;
125        }
126    }
127    *ifap = ifah;
128    closedir(d);
129    ifc_close();
130    return 0;
131}
132
133void freeifaddrs(struct ifaddrs *ifa)
134{
135    struct ifaddrs *ifp;
136
137    while (ifa) {
138        ifp = ifa;
139        free(ifp->ifa_name);
140        if (ifp->ifa_addr)
141            free(ifp->ifa_addr);
142        if (ifp->ifa_netmask)
143            free(ifp->ifa_netmask);
144        ifa = ifa->ifa_next;
145        free(ifp);
146    }
147}
148