|
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ |
|
2 /* vim: set ts=2 et sw=2 tw=80: */ |
|
3 /* |
|
4 * Copyright (C) 2009 The Android Open Source Project |
|
5 * |
|
6 * Licensed under the Apache License, Version 2.0 (the "License"); |
|
7 * you may not use this file except in compliance with the License. |
|
8 * You may obtain a copy of the License at |
|
9 * |
|
10 * http://www.apache.org/licenses/LICENSE-2.0 |
|
11 * |
|
12 * Unless required by applicable law or agreed to in writing, software |
|
13 * distributed under the License is distributed on an "AS IS" BASIS, |
|
14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|
15 * See the License for the specific language governing permissions and |
|
16 * limitations under the License. |
|
17 */ |
|
18 |
|
19 #include "ifaddrs-android-ext.h" |
|
20 |
|
21 #include <stdlib.h> |
|
22 #include <string.h> |
|
23 #include "ScopedFd.h" |
|
24 #include "LocalArray.h" |
|
25 |
|
26 // Returns a pointer to the first byte in the address data (which is |
|
27 // stored in network byte order). |
|
28 uint8_t* sockaddrBytes(int family, sockaddr_storage* ss) { |
|
29 if (family == AF_INET) { |
|
30 sockaddr_in* ss4 = reinterpret_cast<sockaddr_in*>(ss); |
|
31 return reinterpret_cast<uint8_t*>(&ss4->sin_addr); |
|
32 } else if (family == AF_INET6) { |
|
33 sockaddr_in6* ss6 = reinterpret_cast<sockaddr_in6*>(ss); |
|
34 return reinterpret_cast<uint8_t*>(&ss6->sin6_addr); |
|
35 } |
|
36 return NULL; |
|
37 } |
|
38 |
|
39 // Sadly, we can't keep the interface index for portability with BSD. |
|
40 // We'll have to keep the name instead, and re-query the index when |
|
41 // we need it later. |
|
42 bool ifa_setNameAndFlagsByIndex(ifaddrs *self, int interfaceIndex) { |
|
43 // Get the name. |
|
44 char buf[IFNAMSIZ]; |
|
45 char* name = if_indextoname(interfaceIndex, buf); |
|
46 if (name == NULL) { |
|
47 return false; |
|
48 } |
|
49 self->ifa_name = new char[strlen(name) + 1]; |
|
50 strcpy(self->ifa_name, name); |
|
51 |
|
52 // Get the flags. |
|
53 ScopedFd fd(socket(AF_INET, SOCK_DGRAM, 0)); |
|
54 if (fd.get() == -1) { |
|
55 return false; |
|
56 } |
|
57 ifreq ifr; |
|
58 memset(&ifr, 0, sizeof(ifr)); |
|
59 strcpy(ifr.ifr_name, name); |
|
60 int rc = ioctl(fd.get(), SIOCGIFFLAGS, &ifr); |
|
61 if (rc == -1) { |
|
62 return false; |
|
63 } |
|
64 self->ifa_flags = ifr.ifr_flags; |
|
65 return true; |
|
66 } |
|
67 |
|
68 // Netlink gives us the address family in the header, and the |
|
69 // sockaddr_in or sockaddr_in6 bytes as the payload. We need to |
|
70 // stitch the two bits together into the sockaddr that's part of |
|
71 // our portable interface. |
|
72 void ifa_setAddress(ifaddrs *self, int family, void* data, size_t byteCount) { |
|
73 // Set the address proper... |
|
74 sockaddr_storage* ss = new sockaddr_storage; |
|
75 memset(ss, 0, sizeof(*ss)); |
|
76 self->ifa_addr = reinterpret_cast<sockaddr*>(ss); |
|
77 ss->ss_family = family; |
|
78 uint8_t* dst = sockaddrBytes(family, ss); |
|
79 memcpy(dst, data, byteCount); |
|
80 } |
|
81 |
|
82 // Netlink gives us the prefix length as a bit count. We need to turn |
|
83 // that into a BSD-compatible netmask represented by a sockaddr*. |
|
84 void ifa_setNetmask(ifaddrs *self, int family, size_t prefixLength) { |
|
85 // ...and work out the netmask from the prefix length. |
|
86 sockaddr_storage* ss = new sockaddr_storage; |
|
87 memset(ss, 0, sizeof(*ss)); |
|
88 self->ifa_netmask = reinterpret_cast<sockaddr*>(ss); |
|
89 ss->ss_family = family; |
|
90 uint8_t* dst = sockaddrBytes(family, ss); |
|
91 memset(dst, 0xff, prefixLength / 8); |
|
92 if ((prefixLength % 8) != 0) { |
|
93 dst[prefixLength/8] = (0xff << (8 - (prefixLength % 8))); |
|
94 } |
|
95 } |
|
96 |
|
97 // FIXME: use iovec instead. |
|
98 struct addrReq_struct { |
|
99 nlmsghdr netlinkHeader; |
|
100 ifaddrmsg msg; |
|
101 }; |
|
102 |
|
103 inline bool sendNetlinkMessage(int fd, const void* data, size_t byteCount) { |
|
104 ssize_t sentByteCount = TEMP_FAILURE_RETRY(send(fd, data, byteCount, 0)); |
|
105 return (sentByteCount == static_cast<ssize_t>(byteCount)); |
|
106 } |
|
107 |
|
108 inline ssize_t recvNetlinkMessage(int fd, char* buf, size_t byteCount) { |
|
109 return TEMP_FAILURE_RETRY(recv(fd, buf, byteCount, 0)); |
|
110 } |
|
111 |
|
112 // Source-compatible with the BSD function. |
|
113 int getifaddrs(ifaddrs** result) |
|
114 { |
|
115 // Simplify cleanup for callers. |
|
116 *result = NULL; |
|
117 |
|
118 // Create a netlink socket. |
|
119 ScopedFd fd(socket(AF_NETLINK, SOCK_DGRAM, NETLINK_ROUTE)); |
|
120 if (fd.get() < 0) { |
|
121 return -1; |
|
122 } |
|
123 |
|
124 // Ask for the address information. |
|
125 addrReq_struct addrRequest; |
|
126 memset(&addrRequest, 0, sizeof(addrRequest)); |
|
127 addrRequest.netlinkHeader.nlmsg_flags = NLM_F_REQUEST | NLM_F_MATCH; |
|
128 addrRequest.netlinkHeader.nlmsg_type = RTM_GETADDR; |
|
129 addrRequest.netlinkHeader.nlmsg_len = NLMSG_ALIGN(NLMSG_LENGTH(sizeof(addrRequest))); |
|
130 addrRequest.msg.ifa_family = AF_UNSPEC; // All families. |
|
131 addrRequest.msg.ifa_index = 0; // All interfaces. |
|
132 if (!sendNetlinkMessage(fd.get(), &addrRequest, addrRequest.netlinkHeader.nlmsg_len)) { |
|
133 return -1; |
|
134 } |
|
135 |
|
136 // Read the responses. |
|
137 LocalArray<0> buf(65536); // We don't necessarily have std::vector. |
|
138 ssize_t bytesRead; |
|
139 while ((bytesRead = recvNetlinkMessage(fd.get(), &buf[0], buf.size())) > 0) { |
|
140 nlmsghdr* hdr = reinterpret_cast<nlmsghdr*>(&buf[0]); |
|
141 for (; NLMSG_OK(hdr, (size_t)bytesRead); hdr = NLMSG_NEXT(hdr, bytesRead)) { |
|
142 switch (hdr->nlmsg_type) { |
|
143 case NLMSG_DONE: |
|
144 return 0; |
|
145 case NLMSG_ERROR: |
|
146 return -1; |
|
147 case RTM_NEWADDR: |
|
148 { |
|
149 ifaddrmsg* address = reinterpret_cast<ifaddrmsg*>(NLMSG_DATA(hdr)); |
|
150 rtattr* rta = IFA_RTA(address); |
|
151 size_t ifaPayloadLength = IFA_PAYLOAD(hdr); |
|
152 while (RTA_OK(rta, ifaPayloadLength)) { |
|
153 if (rta->rta_type == IFA_LOCAL) { |
|
154 int family = address->ifa_family; |
|
155 if (family == AF_INET || family == AF_INET6) { |
|
156 ifaddrs *next = *result; |
|
157 *result = new ifaddrs; |
|
158 memset(*result, 0, sizeof(ifaddrs)); |
|
159 (*result)->ifa_next = next; |
|
160 if (!ifa_setNameAndFlagsByIndex(*result, address->ifa_index)) { |
|
161 return -1; |
|
162 } |
|
163 ifa_setAddress(*result, family, RTA_DATA(rta), RTA_PAYLOAD(rta)); |
|
164 ifa_setNetmask(*result, family, address->ifa_prefixlen); |
|
165 } |
|
166 } |
|
167 rta = RTA_NEXT(rta, ifaPayloadLength); |
|
168 } |
|
169 } |
|
170 break; |
|
171 } |
|
172 } |
|
173 } |
|
174 // We only get here if recv fails before we see a NLMSG_DONE. |
|
175 return -1; |
|
176 } |
|
177 |
|
178 // Source-compatible with the BSD function. |
|
179 void freeifaddrs(ifaddrs* addresses) { |
|
180 ifaddrs* self = addresses; |
|
181 while (self != NULL) { |
|
182 delete[] self->ifa_name; |
|
183 delete self->ifa_addr; |
|
184 delete self->ifa_netmask; |
|
185 ifaddrs* next = self->ifa_next; |
|
186 delete self; |
|
187 self = next; |
|
188 } |
|
189 } |