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, mask, flags;
36    struct ifaddrs *ifa;
37    struct sockaddr_in *saddr = NULL;
38    struct sockaddr_in *smask = NULL;
39    struct sockaddr_ll *hwaddr = NULL;
40    unsigned char hwbuf[ETH_ALEN];
41
42    if(ifc_get_info(name, &addr, &mask, &flags))
43        return NULL;
44
45    if ((family == AF_INET) && (addr == 0))
46        return NULL;
47
48    ifa = malloc(sizeof(struct ifaddrs));
49    if (!ifa)
50        return NULL;
51    memset(ifa, 0, sizeof(struct ifaddrs));
52
53    ifa->ifa_name = malloc(strlen(name)+1);
54    if (!ifa->ifa_name) {
55        free(ifa);
56        return NULL;
57    }
58    strcpy(ifa->ifa_name, name);
59    ifa->ifa_flags = flags;
60
61    if (family == AF_INET) {
62        saddr = malloc(sizeof(struct sockaddr_in));
63        if (saddr) {
64            saddr->sin_addr.s_addr = addr;
65            saddr->sin_family = family;
66        }
67        ifa->ifa_addr = (struct sockaddr *)saddr;
68
69        if (mask != 0) {
70            smask = malloc(sizeof(struct sockaddr_in));
71            if (smask) {
72                smask->sin_addr.s_addr = mask;
73                smask->sin_family = family;
74            }
75        }
76        ifa->ifa_netmask = (struct sockaddr *)smask;
77    } else if (family == AF_PACKET) {
78        if (!ifc_get_hwaddr(name, hwbuf)) {
79            hwaddr = malloc(sizeof(struct sockaddr_ll));
80            if (hwaddr) {
81                memset(hwaddr, 0, sizeof(struct sockaddr_ll));
82                hwaddr->sll_family = family;
83                /* hwaddr->sll_protocol = ETHERTYPE_IP; */
84                hwaddr->sll_hatype = ARPHRD_ETHER;
85                hwaddr->sll_halen = ETH_ALEN;
86                memcpy(hwaddr->sll_addr, hwbuf, ETH_ALEN);
87            }
88        }
89        ifa->ifa_addr = (struct sockaddr *)hwaddr;
90        ifa->ifa_netmask = (struct sockaddr *)smask;
91    }
92    return ifa;
93}
94
95int getifaddrs(struct ifaddrs **ifap)
96{
97    DIR *d;
98    struct dirent *de;
99    struct ifaddrs *ifa;
100    struct ifaddrs *ifah = NULL;
101
102    if (!ifap)
103        return -1;
104    *ifap = NULL;
105
106    if (ifc_init())
107       return -1;
108
109    d = opendir("/sys/class/net");
110    if (d == 0)
111        return -1;
112    while ((de = readdir(d))) {
113        if (de->d_name[0] == '.')
114            continue;
115        ifa = get_interface(de->d_name, AF_INET);
116        if (ifa != NULL) {
117            ifa->ifa_next = ifah;
118            ifah = ifa;
119        }
120        ifa = get_interface(de->d_name, AF_PACKET);
121        if (ifa != NULL) {
122            ifa->ifa_next = ifah;
123            ifah = ifa;
124        }
125    }
126    *ifap = ifah;
127    closedir(d);
128    ifc_close();
129    return 0;
130}
131
132void freeifaddrs(struct ifaddrs *ifa)
133{
134    struct ifaddrs *ifp;
135
136    while (ifa) {
137        ifp = ifa;
138        free(ifp->ifa_name);
139        if (ifp->ifa_addr)
140            free(ifp->ifa_addr);
141        if (ifp->ifa_netmask)
142            free(ifp->ifa_netmask);
143        ifa = ifa->ifa_next;
144        free(ifp);
145    }
146}
147