/src/wpantund/src/util/TunnelIPv6Interface.cpp
Line | Count | Source |
1 | | /* |
2 | | * |
3 | | * Copyright (c) 2016 Nest Labs, Inc. |
4 | | * All rights reserved. |
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 | | * Description: |
19 | | * This file contains the implementation for a C++ wrapper around the |
20 | | * `tunnel.c`/`tunnel.h` interface. |
21 | | * |
22 | | */ |
23 | | |
24 | | #if HAVE_CONFIG_H |
25 | | #include <config.h> |
26 | | #endif |
27 | | |
28 | | #include "assert-macros.h" |
29 | | #include "TunnelIPv6Interface.h" |
30 | | #include <syslog.h> |
31 | | #include "IPv6Helpers.h" |
32 | | |
33 | | #if __linux__ |
34 | | #include <asm/types.h> |
35 | | #include <linux/if_link.h> |
36 | | #include <sys/socket.h> |
37 | | #include <linux/netlink.h> |
38 | | #include <linux/rtnetlink.h> |
39 | | #include <fcntl.h> |
40 | | #include <ifaddrs.h> |
41 | | #endif |
42 | | |
43 | | #include <sys/select.h> |
44 | | |
45 | | #ifndef O_NONBLOCK |
46 | | #define O_NONBLOCK O_NDELAY |
47 | | #endif |
48 | | |
49 | | namespace { |
50 | | |
51 | | const char* kMLDv2MulticastAddress= "ff02::16"; |
52 | | |
53 | | const char *kFilterMulticastAddresses[] = { |
54 | | kMLDv2MulticastAddress, |
55 | | "ff02::01", //Link local all nodes |
56 | | "ff02::02", //Link local all routers |
57 | | "ff03::01", //realm local all nodes |
58 | | "ff03::02", //realm local all routers |
59 | | "ff03::fc", //realm local all mpl |
60 | | }; |
61 | | |
62 | | struct MLDv2Header { |
63 | | uint8_t mType; |
64 | | uint8_t _rsv0; |
65 | | uint16_t mChecksum; |
66 | | uint16_t _rsv1; |
67 | | uint16_t mNumRecords; |
68 | | } __attribute__((packed)); |
69 | | |
70 | | struct MLDv2Record { |
71 | | uint8_t mRecordType; |
72 | | uint8_t mAuxDataLen; |
73 | | uint16_t mNumSources; |
74 | | struct in6_addr mMulticastAddress; |
75 | | struct in6_addr mSourceAddresses[]; |
76 | | } __attribute__((packed)); |
77 | | |
78 | | } // namespace |
79 | | |
80 | | static bool |
81 | | is_addr_multicast(const struct in6_addr &address) |
82 | 0 | { |
83 | 0 | return (address.s6_addr[0] == 0xff); |
84 | 0 | } |
85 | | |
86 | | TunnelIPv6Interface::TunnelIPv6Interface(const std::string& interface_name, int mtu): |
87 | | #if FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION |
88 | 13.9k | UnixSocket(open("/dev/null",O_RDWR), true), |
89 | | #else |
90 | | UnixSocket(tunnel_open(interface_name.c_str()), true), |
91 | | #endif |
92 | 13.9k | mInterfaceName(interface_name), |
93 | 13.9k | mLastError(0), |
94 | 13.9k | mNetlinkFD(-1), |
95 | | #if FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION |
96 | 13.9k | mNetifMgmtFD(-1), |
97 | | #else |
98 | | mNetifMgmtFD(netif_mgmt_open()), |
99 | | #endif |
100 | 13.9k | mMLDMonitorFD(-1), |
101 | 13.9k | mIsRunning(false), |
102 | 13.9k | mIsUp(false) |
103 | 13.9k | { |
104 | 13.9k | if (0 > mFDRead) { |
105 | 0 | throw std::invalid_argument("Unable to open tunnel interface"); |
106 | 0 | } |
107 | | |
108 | | #if !FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION |
109 | | { |
110 | | char ActualInterfaceName[TUNNEL_MAX_INTERFACE_NAME_LEN] = ""; |
111 | | int ret = 0; |
112 | | ret = tunnel_get_name(mFDRead, ActualInterfaceName, sizeof(ActualInterfaceName)); |
113 | | if (ret) { |
114 | | syslog(LOG_WARNING, |
115 | | "TunnelIPv6Interface: Couldn't get tunnel name! errno=%d, %s", |
116 | | errno, |
117 | | strerror(errno)); |
118 | | } else if (mInterfaceName != ActualInterfaceName) { |
119 | | syslog(LOG_WARNING, |
120 | | "TunnelIPv6Interface: Couldn't create tunnel named \"%s\", got \"%s\" instead!", |
121 | | mInterfaceName.c_str(), |
122 | | ActualInterfaceName); |
123 | | mInterfaceName = ActualInterfaceName; |
124 | | } |
125 | | } |
126 | | |
127 | | netif_mgmt_set_mtu(mNetifMgmtFD, mInterfaceName.c_str(), mtu); |
128 | | |
129 | | setup_signals(); |
130 | | setup_mld_listener(); |
131 | | #endif |
132 | 13.9k | } |
133 | | |
134 | | TunnelIPv6Interface::~TunnelIPv6Interface() |
135 | 13.9k | { |
136 | 13.9k | close(mNetlinkFD); |
137 | 13.9k | if (mMLDMonitorFD >= 0) { |
138 | 0 | close(mMLDMonitorFD); |
139 | 0 | } |
140 | 13.9k | netif_mgmt_close(mNetifMgmtFD); |
141 | 13.9k | } |
142 | | |
143 | | void |
144 | | TunnelIPv6Interface::on_link_state_changed(bool isUp, bool isRunning) |
145 | 0 | { |
146 | 0 | syslog(LOG_INFO, "TunnelIPv6Interface::on_link_state_changed() UP=%d RUNNING=%d", isUp, isRunning); |
147 | 0 | if (isRunning != mIsRunning || isUp != mIsUp) { |
148 | 0 | if (isRunning && !mIsRunning) { |
149 | 0 | std::map<struct in6_addr, Entry>::iterator iter; |
150 | |
|
151 | 0 | for (iter = mUnicastAddresses.begin(); iter != mUnicastAddresses.end(); ++iter) { |
152 | 0 | if (iter->second.mState != Entry::kWaitingToAdd) { |
153 | 0 | continue; |
154 | 0 | } |
155 | | |
156 | 0 | syslog(LOG_INFO, "Adding address \"%s/%d\" to interface \"%s\"", |
157 | 0 | in6_addr_to_string(iter->first).c_str(), iter->second.mPrefixLen, |
158 | 0 | mInterfaceName.c_str()); |
159 | |
|
160 | 0 | IGNORE_RETURN_VALUE(netif_mgmt_add_ipv6_address(mNetifMgmtFD, mInterfaceName.c_str(), |
161 | 0 | iter->first.s6_addr, iter->second.mPrefixLen)); |
162 | 0 | iter->second.mState = Entry::kWaitingForAddConfirm; |
163 | 0 | } |
164 | |
|
165 | 0 | for (iter = mPendingMulticastAddresses.begin(); iter != mPendingMulticastAddresses.end(); ++iter) { |
166 | 0 | if (iter->second.mState != Entry::kWaitingToAdd) { |
167 | 0 | continue; |
168 | 0 | } |
169 | | |
170 | 0 | syslog(LOG_INFO, "Joining multicast address \"%s\" on interface \"%s\".", |
171 | 0 | in6_addr_to_string(iter->first).c_str(), mInterfaceName.c_str()); |
172 | |
|
173 | 0 | IGNORE_RETURN_VALUE(netif_mgmt_join_ipv6_multicast_address(mNetifMgmtFD, mInterfaceName.c_str(), |
174 | 0 | iter->first.s6_addr)); |
175 | 0 | } |
176 | 0 | mPendingMulticastAddresses.clear(); |
177 | 0 | } |
178 | 0 | mIsUp = isUp; |
179 | 0 | mIsRunning = isRunning; |
180 | 0 | mLinkStateChanged(isUp, isRunning); |
181 | 0 | } |
182 | 0 | } |
183 | | |
184 | | void |
185 | | TunnelIPv6Interface::on_address_added(const struct in6_addr &address, uint8_t prefix_len) |
186 | 0 | { |
187 | 0 | if (mUnicastAddresses.count(address)) { |
188 | 0 | mUnicastAddresses.erase(address); |
189 | 0 | } |
190 | |
|
191 | 0 | syslog(LOG_INFO, "TunnelIPv6Interface: \"%s/%d\" was added to \"%s\"", in6_addr_to_string(address).c_str(), prefix_len, |
192 | 0 | get_interface_name().c_str()); |
193 | |
|
194 | 0 | mUnicastAddressWasAdded(address, prefix_len); |
195 | 0 | } |
196 | | |
197 | | void |
198 | | TunnelIPv6Interface::on_multicast_address_joined(const struct in6_addr &address) |
199 | 0 | { |
200 | 0 | if (mPendingMulticastAddresses.count(address)) { |
201 | 0 | mPendingMulticastAddresses.erase(address); |
202 | 0 | } |
203 | |
|
204 | 0 | syslog(LOG_INFO, "TunnelIPv6Interface: \"%s\" was added to \"%s\"", in6_addr_to_string(address).c_str(), |
205 | 0 | get_interface_name().c_str()); |
206 | |
|
207 | 0 | mMulticastAddressWasJoined(address); |
208 | 0 | } |
209 | | |
210 | | void |
211 | | TunnelIPv6Interface::on_address_removed(const struct in6_addr &address, uint8_t prefix_len) |
212 | 0 | { |
213 | | // Ignore "removed" signal if address is the list |
214 | | // meaning it was added earlier and we are still |
215 | | // waiting to confirm that it is added. |
216 | | // This is to address the case where before adding an |
217 | | // address on interface it may be first removed. |
218 | |
|
219 | 0 | if (!mUnicastAddresses.count(address)) { |
220 | 0 | syslog(LOG_INFO, "TunnelIPv6Interface: \"%s/%d\" was removed from \"%s\"", in6_addr_to_string(address).c_str(), prefix_len, |
221 | 0 | get_interface_name().c_str()); |
222 | |
|
223 | 0 | mUnicastAddressWasRemoved(address, prefix_len); |
224 | 0 | } |
225 | |
|
226 | 0 | } |
227 | | |
228 | | void |
229 | | TunnelIPv6Interface::on_multicast_address_left(const struct in6_addr &address) |
230 | 0 | { |
231 | | // Ignore "removed" signal if address is the list |
232 | | // meaning it was added earlier and we are still |
233 | | // waiting to confirm that it is added. |
234 | | // This is to address the case where before adding an |
235 | | // address on interface it may be first removed. |
236 | |
|
237 | 0 | if (!mPendingMulticastAddresses.count(address)) { |
238 | 0 | syslog(LOG_INFO, "TunnelIPv6Interface: \"%s\" was removed from \"%s\"", in6_addr_to_string(address).c_str(), |
239 | 0 | get_interface_name().c_str()); |
240 | |
|
241 | 0 | mMulticastAddressWasLeft(address); |
242 | 0 | } |
243 | 0 | } |
244 | | |
245 | | #if __linux__ // -------------------------------------------------------------- |
246 | | |
247 | 0 | #define LCG32(x) ((uint32_t)(x)*1664525+1013904223) |
248 | | |
249 | | void |
250 | | TunnelIPv6Interface::setup_signals(void) |
251 | 0 | { |
252 | 0 | int status; |
253 | 0 | int fd; |
254 | 0 | struct sockaddr_nl la; |
255 | |
|
256 | 0 | fd = socket(AF_NETLINK, SOCK_DGRAM, NETLINK_ROUTE); |
257 | |
|
258 | 0 | require(fd != -1, bail); |
259 | | |
260 | 0 | memset(&la, 0, sizeof(la)); |
261 | 0 | la.nl_family = AF_NETLINK; |
262 | 0 | la.nl_pad = 0; |
263 | 0 | la.nl_groups = RTMGRP_LINK | RTMGRP_IPV6_IFADDR; |
264 | | |
265 | | // We calculate the PID in a pseudo-random way based on the |
266 | | // address pointer and the actual process ID. |
267 | 0 | la.nl_pid = LCG32(getpid()) ^ LCG32((uint32_t)reinterpret_cast<uintptr_t>(this)); |
268 | |
|
269 | 0 | status = bind(fd, (struct sockaddr*) &la, sizeof(la)); |
270 | |
|
271 | 0 | require(status != -1, bail); |
272 | | |
273 | | // Success! |
274 | 0 | IGNORE_RETURN_VALUE(fcntl(fd, F_SETFL, O_NONBLOCK)); |
275 | |
|
276 | 0 | mNetlinkFD = fd; |
277 | 0 | fd = -1; |
278 | |
|
279 | 0 | bail: |
280 | | |
281 | | // Cleanup (If necessary) |
282 | 0 | if (fd > 0) { |
283 | 0 | close(fd); |
284 | 0 | } |
285 | |
|
286 | 0 | return; |
287 | 0 | } |
288 | | |
289 | | void |
290 | | TunnelIPv6Interface::setup_mld_listener(void) |
291 | 0 | { |
292 | 0 | unsigned interfaceIndex = netif_mgmt_get_ifindex(mNetifMgmtFD, mInterfaceName.c_str()); |
293 | 0 | bool success = false; |
294 | 0 | struct ipv6_mreq mreq6; |
295 | |
|
296 | 0 | mMLDMonitorFD = socket(AF_INET6, SOCK_RAW | SOCK_NONBLOCK, IPPROTO_ICMPV6); |
297 | 0 | mreq6.ipv6mr_interface = interfaceIndex; |
298 | 0 | inet_pton(AF_INET6, kMLDv2MulticastAddress, &mreq6.ipv6mr_multiaddr); |
299 | |
|
300 | 0 | require(setsockopt(mMLDMonitorFD, IPPROTO_IPV6, IPV6_JOIN_GROUP, &mreq6, sizeof(mreq6)) == 0, bail); |
301 | 0 | require(setsockopt(mMLDMonitorFD, SOL_SOCKET, SO_BINDTODEVICE, mInterfaceName.c_str(), mInterfaceName.size()) == 0, bail); |
302 | | |
303 | 0 | success = true; |
304 | |
|
305 | 0 | bail: |
306 | 0 | if (!success) { |
307 | 0 | if (mMLDMonitorFD >= 0) { |
308 | 0 | close(mMLDMonitorFD); |
309 | 0 | } |
310 | 0 | syslog(LOG_ERR, "listen to MLD messages on interface failed\n"); |
311 | 0 | } |
312 | 0 | return; |
313 | 0 | } |
314 | | |
315 | | int |
316 | | TunnelIPv6Interface::process(void) |
317 | 2.90M | { |
318 | 2.90M | processNetlinkFD(); |
319 | 2.90M | processMLDMonitorFD(); |
320 | | |
321 | 2.90M | return nl::UnixSocket::process(); |
322 | 2.90M | } |
323 | | |
324 | | void |
325 | | TunnelIPv6Interface::processNetlinkFD(void) |
326 | 2.90M | { |
327 | 2.90M | uint8_t buffer[4096]; |
328 | 2.90M | ssize_t buffer_len(-1); |
329 | | |
330 | 2.90M | if (mNetlinkFD >= 0) { |
331 | 0 | buffer_len = recv(mNetlinkFD, buffer, sizeof(buffer), 0); |
332 | 0 | } |
333 | | |
334 | 2.90M | if (buffer_len > 0) { |
335 | 0 | struct nlmsghdr *nlp; |
336 | 0 | struct rtmsg *rtp; |
337 | 0 | int rta_len; |
338 | 0 | struct rtattr *rta; |
339 | |
|
340 | 0 | nlp = (struct nlmsghdr *)buffer; |
341 | 0 | for (;NLMSG_OK(nlp, buffer_len); nlp=NLMSG_NEXT(nlp, buffer_len)) |
342 | 0 | { |
343 | 0 | char ifnamebuf[IF_NAMESIZE]; |
344 | 0 | if (nlp->nlmsg_type == RTM_NEWADDR || nlp->nlmsg_type == RTM_DELADDR) { |
345 | 0 | struct ifaddrmsg *ifaddr = (struct ifaddrmsg *)NLMSG_DATA(nlp); |
346 | 0 | const char *ifname = if_indextoname(ifaddr->ifa_index, ifnamebuf); |
347 | 0 | struct in6_addr addr; |
348 | |
|
349 | 0 | if ((ifname == NULL) || (get_interface_name() != ifname)) { |
350 | 0 | continue; |
351 | 0 | } |
352 | | |
353 | | // get RTNETLINK message header |
354 | | // get start of attributes |
355 | 0 | rta = (struct rtattr *) IFA_RTA(ifaddr); |
356 | | |
357 | | // get length of attributes |
358 | 0 | rta_len = IFA_PAYLOAD(nlp); |
359 | |
|
360 | 0 | for(;RTA_OK(rta, rta_len); rta = RTA_NEXT(rta, rta_len)) { |
361 | 0 | switch(rta->rta_type) { |
362 | 0 | case IFA_ADDRESS: |
363 | 0 | case IFA_LOCAL: |
364 | 0 | case IFA_BROADCAST: |
365 | 0 | case IFA_ANYCAST: |
366 | 0 | memcpy(addr.s6_addr, RTA_DATA(rta), sizeof(addr)); |
367 | |
|
368 | 0 | if (nlp->nlmsg_type == RTM_NEWADDR) { |
369 | 0 | on_address_added(addr, ifaddr->ifa_prefixlen); |
370 | 0 | } else if (nlp->nlmsg_type == RTM_DELADDR) { |
371 | 0 | on_address_removed(addr, ifaddr->ifa_prefixlen); |
372 | 0 | } |
373 | 0 | break; |
374 | 0 | default: |
375 | 0 | break; |
376 | 0 | } |
377 | 0 | } |
378 | 0 | } else if (nlp->nlmsg_type == RTM_NEWLINK || nlp->nlmsg_type == RTM_DELLINK) { |
379 | 0 | struct ifinfomsg *ifinfo = (struct ifinfomsg *)NLMSG_DATA(nlp); |
380 | 0 | const char *ifname = if_indextoname(ifinfo->ifi_index, ifnamebuf); |
381 | 0 | bool isUp, isRunning; |
382 | |
|
383 | 0 | if ((ifname == NULL) || (get_interface_name() != ifname)) { |
384 | 0 | continue; |
385 | 0 | } |
386 | | |
387 | 0 | isUp = ((ifinfo->ifi_flags & IFF_UP) == IFF_UP); |
388 | 0 | isRunning = ((ifinfo->ifi_flags & IFF_RUNNING) == IFF_RUNNING); |
389 | |
|
390 | 0 | on_link_state_changed(isUp, isRunning); |
391 | 0 | } |
392 | 0 | } |
393 | 0 | } |
394 | 2.90M | } |
395 | | |
396 | | static bool IsMulticastAddressFiltered(const struct in6_addr& addr_to_check) |
397 | 0 | { |
398 | 0 | bool found = false; |
399 | |
|
400 | 0 | for (size_t i = 0; i < sizeof(kFilterMulticastAddresses) / |
401 | 0 | sizeof(kFilterMulticastAddresses[0]); |
402 | 0 | i++) { |
403 | 0 | struct in6_addr addr; |
404 | |
|
405 | 0 | inet_pton(AF_INET6, kFilterMulticastAddresses[i], &addr); |
406 | 0 | if (memcmp(&addr, &addr_to_check, sizeof(addr)) == 0) { |
407 | 0 | found = true; |
408 | 0 | break; |
409 | 0 | } |
410 | 0 | } |
411 | |
|
412 | 0 | return found; |
413 | 0 | } |
414 | | |
415 | | void |
416 | | TunnelIPv6Interface::processMLDMonitorFD(void) |
417 | 2.90M | { |
418 | 2.90M | uint8_t buffer[4096]; |
419 | 2.90M | ssize_t bufferLen(-1); |
420 | 2.90M | struct sockaddr_in6 srcAddr; |
421 | 2.90M | socklen_t addrLen; |
422 | 2.90M | bool fromSelf = false; |
423 | 2.90M | MLDv2Header* hdr = reinterpret_cast<MLDv2Header *>(buffer); |
424 | 2.90M | ssize_t offset; |
425 | 2.90M | uint8_t type; |
426 | 2.90M | struct ifaddrs *ifAddrs = NULL; |
427 | | |
428 | 2.90M | if (mNetlinkFD >= 0) { |
429 | 0 | bufferLen = recvfrom(mMLDMonitorFD, buffer, sizeof(buffer), 0, reinterpret_cast<sockaddr *>(&srcAddr), &addrLen); |
430 | 0 | } |
431 | 2.90M | require_quiet(bufferLen > 0, bail); |
432 | | |
433 | 0 | type = buffer[0]; |
434 | 0 | require_quiet(type == kICMPv6MLDv2Type && bufferLen >= sizeof(MLDv2Header), bail); |
435 | | |
436 | | // Check whether it is sent by self |
437 | 0 | require(getifaddrs(&ifAddrs) == 0, bail); |
438 | 0 | for (struct ifaddrs* ifAddr = ifAddrs; ifAddr != NULL; ifAddr = ifAddr->ifa_next) { |
439 | 0 | if (ifAddr->ifa_addr != NULL && ifAddr->ifa_addr->sa_family == AF_INET6 && |
440 | 0 | mInterfaceName == std::string(ifAddr->ifa_name)) { |
441 | 0 | struct sockaddr_in6 *addr6 = reinterpret_cast<struct sockaddr_in6 *>(ifAddr->ifa_addr); |
442 | |
|
443 | 0 | if (memcmp(&addr6->sin6_addr, &srcAddr.sin6_addr, sizeof(in6_addr)) == 0) { |
444 | 0 | fromSelf = true; |
445 | 0 | break; |
446 | 0 | } |
447 | 0 | } |
448 | 0 | } |
449 | 0 | require_quiet(fromSelf, bail); |
450 | | |
451 | 0 | hdr = reinterpret_cast<MLDv2Header *>(buffer); |
452 | 0 | offset = sizeof(MLDv2Header); |
453 | |
|
454 | 0 | for (size_t i = 0; i < ntohs(hdr->mNumRecords) && offset < bufferLen; i++) { |
455 | 0 | if (bufferLen - offset >= sizeof(MLDv2Record)) { |
456 | 0 | MLDv2Record *record = reinterpret_cast<MLDv2Record *>(&buffer[offset]); |
457 | |
|
458 | 0 | if (!IsMulticastAddressFiltered(record->mMulticastAddress)) { |
459 | 0 | if (record->mRecordType == kICMPv6MLDv2RecordChangeToIncludeType) { |
460 | 0 | on_multicast_address_joined(record->mMulticastAddress); |
461 | 0 | } else if (record->mRecordType == kICMPv6MLDv2RecordChangeToExcludeType) { |
462 | 0 | on_multicast_address_left(record->mMulticastAddress); |
463 | 0 | } |
464 | 0 | } |
465 | |
|
466 | 0 | offset += sizeof(MLDv2Record) + sizeof(in6_addr) * ntohs(record->mNumSources); |
467 | 0 | } |
468 | 0 | } |
469 | |
|
470 | 2.90M | bail: |
471 | 2.90M | if (ifAddrs) { |
472 | 0 | freeifaddrs(ifAddrs); |
473 | 0 | } |
474 | | |
475 | 2.90M | return; |
476 | 0 | } |
477 | | |
478 | | #else // ---------------------------------------------------------------------- |
479 | | |
480 | | void |
481 | | TunnelIPv6Interface::setup_signals(void) |
482 | | { |
483 | | // Unknown platform. |
484 | | } |
485 | | |
486 | | int |
487 | | TunnelIPv6Interface::process(void) |
488 | | { |
489 | | return nl::UnixSocket::process(); |
490 | | } |
491 | | |
492 | | void |
493 | | TunnelIPv6Interface::processNetlinkFD(void) |
494 | | { |
495 | | } |
496 | | |
497 | | void |
498 | | TunnelIPv6Interface::processMLDMonitorFD(void) |
499 | | { |
500 | | } |
501 | | |
502 | | #endif // --------------------------------------------------------------------- |
503 | | |
504 | | int |
505 | | TunnelIPv6Interface::update_fd_set(fd_set *read_fd_set, fd_set *write_fd_set, fd_set *error_fd_set, int *max_fd, cms_t *timeout) |
506 | 5.85M | { |
507 | 5.85M | if (read_fd_set) { |
508 | 2.92M | if (mNetlinkFD >= 0) { |
509 | 0 | FD_SET(mNetlinkFD, read_fd_set); |
510 | 0 | } |
511 | | |
512 | 2.92M | if (mMLDMonitorFD >= 0) { |
513 | 0 | FD_SET(mMLDMonitorFD, read_fd_set); |
514 | 0 | } |
515 | | |
516 | 2.92M | if ((max_fd != NULL)) { |
517 | 2.92M | *max_fd = std::max(*max_fd, mNetlinkFD); |
518 | 2.92M | *max_fd = std::max(*max_fd, mMLDMonitorFD); |
519 | 2.92M | } |
520 | 2.92M | } |
521 | | |
522 | 5.85M | return nl::UnixSocket::update_fd_set(read_fd_set, write_fd_set, error_fd_set, max_fd, timeout); |
523 | 5.85M | } |
524 | | |
525 | | const std::string& |
526 | | TunnelIPv6Interface::get_interface_name(void) |
527 | 0 | { |
528 | 0 | return mInterfaceName; |
529 | 0 | } |
530 | | |
531 | | int |
532 | | TunnelIPv6Interface::get_last_error(void) |
533 | 0 | { |
534 | 0 | return mLastError; |
535 | 0 | } |
536 | | |
537 | | bool |
538 | | TunnelIPv6Interface::is_up(void) |
539 | 0 | { |
540 | 0 | return netif_mgmt_is_up(mNetifMgmtFD, mInterfaceName.c_str()); |
541 | 0 | } |
542 | | |
543 | | bool |
544 | | TunnelIPv6Interface::is_running(void) |
545 | 7.45k | { |
546 | 7.45k | return netif_mgmt_is_running(mNetifMgmtFD, mInterfaceName.c_str()); |
547 | 7.45k | } |
548 | | |
549 | | bool |
550 | | TunnelIPv6Interface::is_online(void) |
551 | 31.8k | { |
552 | 31.8k | static const int online_flags = IFF_UP | IFF_RUNNING; |
553 | 31.8k | return (netif_mgmt_get_flags(mNetifMgmtFD, mInterfaceName.c_str()) & online_flags) == online_flags; |
554 | 31.8k | } |
555 | | |
556 | | int |
557 | | TunnelIPv6Interface::set_up(bool isUp) |
558 | 0 | { |
559 | 0 | int ret = 0; |
560 | 0 | bool old = is_up(); |
561 | |
|
562 | 0 | if (isUp != old) { |
563 | 0 | if (isUp) { |
564 | 0 | syslog(LOG_INFO, "Bringing interface %s up. . .", mInterfaceName.c_str()); |
565 | 0 | } else { |
566 | 0 | syslog(LOG_INFO, "Taking interface %s down. . .", mInterfaceName.c_str()); |
567 | 0 | } |
568 | |
|
569 | 0 | ret = netif_mgmt_set_up(mNetifMgmtFD, mInterfaceName.c_str(), isUp); |
570 | |
|
571 | 0 | require_action(ret == 0, bail, mLastError = errno); |
572 | 0 | } |
573 | | |
574 | 0 | bail: |
575 | 0 | return ret; |
576 | 0 | } |
577 | | |
578 | | int |
579 | | TunnelIPv6Interface::set_running(bool isRunning) |
580 | 7.45k | { |
581 | 7.45k | int ret = 0; |
582 | 7.45k | bool old = is_running(); |
583 | | |
584 | 7.45k | if (isRunning != old) { |
585 | 2.35k | if (isRunning) { |
586 | 2.35k | syslog(LOG_INFO, "Bringing interface %s online. . .", mInterfaceName.c_str()); |
587 | 2.35k | } else { |
588 | 0 | syslog(LOG_INFO, "Taking interface %s offline. . .", mInterfaceName.c_str()); |
589 | 0 | } |
590 | | |
591 | 2.35k | ret = netif_mgmt_set_running(mNetifMgmtFD, mInterfaceName.c_str(), isRunning); |
592 | | |
593 | 2.35k | mLastError = errno; |
594 | | |
595 | 2.35k | require_action(ret == 0, bail, mLastError = errno); |
596 | | |
597 | 2.35k | } |
598 | | |
599 | 7.45k | bail: |
600 | 7.45k | return ret; |
601 | 7.45k | } |
602 | | |
603 | | |
604 | | int |
605 | | TunnelIPv6Interface::set_online(bool online) |
606 | 7.45k | { |
607 | 7.45k | return set_running(online); |
608 | 7.45k | } |
609 | | |
610 | | void |
611 | | TunnelIPv6Interface::reset(void) |
612 | 2.99k | { |
613 | 2.99k | syslog(LOG_INFO, "Resetting interface %s. . .", mInterfaceName.c_str()); |
614 | 2.99k | set_online(false); |
615 | 2.99k | } |
616 | | |
617 | | |
618 | | bool |
619 | | TunnelIPv6Interface::add_address(const struct in6_addr *addr, int prefixlen) |
620 | 13.7k | { |
621 | 13.7k | bool ret = false; |
622 | | |
623 | 13.7k | require_action(!IN6_IS_ADDR_UNSPECIFIED(addr), bail, mLastError = EINVAL); |
624 | | |
625 | 13.6k | if (mUnicastAddresses.count(*addr)) { |
626 | 0 | ret = true; |
627 | 0 | goto bail; |
628 | 0 | } |
629 | | |
630 | 13.6k | if (is_online()) { |
631 | 0 | syslog(LOG_INFO, "Adding address \"%s/%d\" to interface \"%s\"", |
632 | 0 | in6_addr_to_string(*addr).c_str(), prefixlen, mInterfaceName.c_str()); |
633 | |
|
634 | 0 | require_noerr_action( |
635 | 0 | netif_mgmt_add_ipv6_address(mNetifMgmtFD, mInterfaceName.c_str(), addr->s6_addr, prefixlen), |
636 | 0 | bail, |
637 | 0 | mLastError = errno |
638 | 0 | ); |
639 | 0 | mUnicastAddresses[*addr] = Entry(Entry::kWaitingForAddConfirm, prefixlen); |
640 | 13.6k | } else { |
641 | 13.6k | mUnicastAddresses[*addr] = Entry(Entry::kWaitingToAdd, prefixlen); |
642 | 13.6k | } |
643 | | |
644 | 13.6k | ret = true; |
645 | | |
646 | 13.7k | bail: |
647 | 13.7k | return ret; |
648 | 13.6k | } |
649 | | |
650 | | bool |
651 | | TunnelIPv6Interface::remove_address(const struct in6_addr *addr, int prefixlen) |
652 | 11.5k | { |
653 | 11.5k | bool ret = false; |
654 | | |
655 | 11.5k | require_action(!IN6_IS_ADDR_UNSPECIFIED(addr), bail, mLastError = EINVAL); |
656 | | |
657 | 11.4k | if (mUnicastAddresses.count(*addr)) { |
658 | 11.4k | mUnicastAddresses.erase(*addr); |
659 | 11.4k | } |
660 | | |
661 | 11.4k | if (netif_mgmt_remove_ipv6_address(mNetifMgmtFD, mInterfaceName.c_str(), addr->s6_addr) != 0) { |
662 | 11.4k | mLastError = errno; |
663 | 11.4k | goto bail; |
664 | 11.4k | } |
665 | | |
666 | 0 | syslog(LOG_INFO,"Removing address \"%s\" from interface \"%s\"", |
667 | 0 | in6_addr_to_string(*addr).c_str(), mInterfaceName.c_str()); |
668 | 0 | ret = true; |
669 | |
|
670 | 11.5k | bail: |
671 | 11.5k | return ret; |
672 | 0 | } |
673 | | |
674 | | bool |
675 | | TunnelIPv6Interface::join_multicast_address(const struct in6_addr *addr) |
676 | 18.2k | { |
677 | 18.2k | bool ret = false; |
678 | | |
679 | 18.2k | require_action(!IN6_IS_ADDR_UNSPECIFIED(addr), bail, mLastError = EINVAL); |
680 | | |
681 | 18.2k | if (is_online()) { |
682 | 0 | if (netif_mgmt_join_ipv6_multicast_address(mNetifMgmtFD, mInterfaceName.c_str(), addr->s6_addr) != 0) { |
683 | 0 | mLastError = errno; |
684 | 0 | goto bail; |
685 | 0 | } |
686 | | |
687 | 0 | syslog(LOG_INFO, "Joining multicast address \"%s\" on interface \"%s\".", |
688 | 0 | in6_addr_to_string(*addr).c_str(), mInterfaceName.c_str()); |
689 | 18.2k | } else { |
690 | 18.2k | mPendingMulticastAddresses[*addr] = Entry(Entry::kWaitingToAdd); |
691 | 18.2k | } |
692 | | |
693 | 18.2k | ret = true; |
694 | | |
695 | 18.2k | bail: |
696 | 18.2k | return ret; |
697 | 18.2k | } |
698 | | |
699 | | bool |
700 | | TunnelIPv6Interface::leave_multicast_address(const struct in6_addr *addr) |
701 | 3.42k | { |
702 | 3.42k | bool ret = false; |
703 | | |
704 | 3.42k | require_action(!IN6_IS_ADDR_UNSPECIFIED(addr), bail, mLastError = EINVAL); |
705 | | |
706 | 3.42k | if (mPendingMulticastAddresses.count(*addr)) { |
707 | 3.42k | mPendingMulticastAddresses.erase(*addr); |
708 | 3.42k | } |
709 | | |
710 | 3.42k | if (netif_mgmt_leave_ipv6_multicast_address(mNetifMgmtFD, mInterfaceName.c_str(), addr->s6_addr) != 0) { |
711 | 3.42k | mLastError = errno; |
712 | 3.42k | goto bail; |
713 | 3.42k | } |
714 | | |
715 | 0 | syslog(LOG_INFO, "Leaving multicast address \"%s\" on interface \"%s\".", |
716 | 0 | in6_addr_to_string(*addr).c_str(), mInterfaceName.c_str()); |
717 | 0 | ret = true; |
718 | |
|
719 | 3.42k | bail: |
720 | 3.42k | return ret; |
721 | 0 | } |
722 | | |
723 | | |
724 | | bool |
725 | | TunnelIPv6Interface::add_route(const struct in6_addr *route, int prefixlen, uint32_t metric) |
726 | 13.8k | { |
727 | 13.8k | bool ret = false; |
728 | | |
729 | 13.8k | if (netif_mgmt_add_ipv6_route(mNetifMgmtFD, mInterfaceName.c_str(), route->s6_addr, prefixlen, metric) != 0) { |
730 | 13.8k | mLastError = errno; |
731 | 13.8k | goto bail; |
732 | 13.8k | } |
733 | 0 | syslog(LOG_INFO, "Adding route prefix \"%s/%d\" on interface \"%s\".", |
734 | 0 | in6_addr_to_string(*route).c_str(), prefixlen, mInterfaceName.c_str()); |
735 | |
|
736 | 0 | ret = true; |
737 | |
|
738 | 13.8k | bail: |
739 | 13.8k | return ret; |
740 | 0 | } |
741 | | |
742 | | bool |
743 | | TunnelIPv6Interface::remove_route(const struct in6_addr *route, int prefixlen, uint32_t metric) |
744 | 11.3k | { |
745 | 11.3k | bool ret = false; |
746 | | |
747 | 11.3k | if (netif_mgmt_remove_ipv6_route(mNetifMgmtFD, mInterfaceName.c_str(), route->s6_addr, prefixlen, metric) != 0) { |
748 | 11.3k | mLastError = errno; |
749 | 11.3k | goto bail; |
750 | 11.3k | } |
751 | | |
752 | 0 | syslog(LOG_INFO, "Removing route prefix \"%s/%d\" on interface \"%s\".", |
753 | 0 | in6_addr_to_string(*route).c_str(), prefixlen, mInterfaceName.c_str()); |
754 | |
|
755 | 0 | ret = true; |
756 | |
|
757 | 11.3k | bail: |
758 | 11.3k | return ret; |
759 | 0 | } |
760 | | |
761 | | ssize_t |
762 | | TunnelIPv6Interface::read(void* data, size_t len) |
763 | 0 | { |
764 | 0 | ssize_t ret = nl::UnixSocket::read(data, len); |
765 | 0 | uint8_t *data_bytes = static_cast<uint8_t*>(data); |
766 | | |
767 | | // Remove any subheader, if present. |
768 | 0 | if ((ret >= 4) && (data_bytes[0] == 0) && (data_bytes[1] == 0)) { |
769 | 0 | ret -= 4; |
770 | 0 | memmove(data, static_cast<const void*>(data_bytes + 4), ret); |
771 | 0 | } |
772 | |
|
773 | 0 | return ret; |
774 | 0 | } |
775 | | |
776 | | ssize_t |
777 | | TunnelIPv6Interface::write(const void* data, size_t len) |
778 | 13.2k | { |
779 | | #ifdef __APPLE__ |
780 | | const uint8_t* const data_bytes = static_cast<const uint8_t*>(data); |
781 | | |
782 | | if ((data_bytes[0] != 0) || (data_bytes[0] != 0)) { |
783 | | // The utun interface on OS X needs this header. |
784 | | // Linux seems to be able to infer the type of the packet |
785 | | // with no problems. |
786 | | uint8_t packet[len + 4]; |
787 | | packet[0] = 0; |
788 | | packet[1] = 0; |
789 | | packet[2] = (PF_INET6 << 8) & 0xFF; |
790 | | packet[3] = (PF_INET6 << 0) & 0xFF; |
791 | | memcpy(static_cast<void*>(packet + 4), data, len); |
792 | | ssize_t ret = nl::UnixSocket::write(packet, len + 4); |
793 | | return (ret >= 4)?(ret - 4):(-1); |
794 | | } |
795 | | #endif |
796 | 13.2k | return nl::UnixSocket::write(data, len); |
797 | 13.2k | } |