master
cpp 758 lines 25.5 KB
Raw
1 // Copyright (C) Microsoft Corporation. All rights reserved.
2 #include <sys/ioctl.h>
3 #include <sys/types.h>
4 #include <arpa/inet.h>
5 #include <net/if.h>
6 #include <linux/netlink.h>
7 #include <linux/nl80211.h>
8 #include <linux/rtnetlink.h>
9 #include <iostream>
10 #include <optional>
11 #include <gsl/gsl>
12 #include <gslhelpers.h>
13 #include <string.h>
14 #include <ifaddrs.h>
15 #include <linux/if_packet.h>
16 #include <linux/pkt_cls.h>
17 #include <lxwil.h>
18 #include <linux/if_tun.h>
19 #include "NetlinkChannel.h"
20 #include "Syscall.h"
21 #include "Utils.h"
22 #include "Interface.h"
23 #include "lxwil.h"
24
25 using utils::Attribute;
26
27 constexpr char c_value0[] = "0\n";
28 constexpr char c_value1[] = "1\n";
29
30 template <typename TAddr>
31 struct AddressMessage
32 {
33 ifaddrmsg ifaddr;
34 utils::AddressAttribute<TAddr> localAddress;
35 utils::AddressAttribute<TAddr> address;
36 utils::CacheInfoAttribute cacheInfo;
37 utils::IntegerAttribute addressFlags;
38 } __attribute__((packed));
39
40 template <typename TAddr>
41 struct AddressMessageWithBroadcast : AddressMessage<TAddr>
42 {
43 utils::AddressAttribute<TAddr> broadcastAddress;
44 } __attribute__((packed));
45
46 Interface::Interface()
47 {
48 }
49
50 Interface::Interface(const Interface& source) : m_index(source.m_index), m_name(source.m_name)
51 {
52 }
53
54 Interface::Interface(int index, const std::string& name) : m_index(index), m_name(name)
55 {
56 }
57
58 Interface::operator bool() const
59 {
60 return m_index != -1;
61 }
62
63 template <typename TAddr>
64 void Interface::ChangeAddress(const Address& address, const std::optional<Address>& broadcastAddress, Operation operation)
65 {
66 if (broadcastAddress.has_value())
67 {
68 ChangeAddressImpl<TAddr, AddressMessageWithBroadcast<TAddr>>(address, broadcastAddress, operation);
69 }
70 else
71 {
72 ChangeAddressImpl<TAddr, AddressMessage<TAddr>>(address, broadcastAddress, operation);
73 }
74 }
75
76 // TMessage must be derived from AddressMessage or one of its children
77 template <typename TAddr, typename TMessage>
78 void Interface::ChangeAddressImpl(const Address& address, const std::optional<Address>& broadcastAddress, Operation operation)
79 {
80 TMessage message{};
81
82 message.ifaddr.ifa_family = address.Family();
83 message.ifaddr.ifa_prefixlen = address.PrefixLength();
84 message.ifaddr.ifa_index = m_index;
85 message.ifaddr.ifa_scope = address.Scope();
86
87 utils::InitializeAddressAttribute<TAddr>(message.address, address, IFA_ADDRESS);
88 utils::InitializeAddressAttribute<TAddr>(message.localAddress, address, IFA_LOCAL);
89 utils::InitializeCacheInfoAttribute(message.cacheInfo, address);
90
91 // For remove, most flags are ignored, including noprefixroute and nodad.
92 int addrFlags = address.IsPrefixRouteAutogenerationDisabled() ? IFA_F_NOPREFIXROUTE : 0;
93 if (address.Family() == AF_INET6)
94 {
95 addrFlags |= IFA_F_NODAD;
96 }
97 utils::InitializeIntegerAttribute(message.addressFlags, addrFlags, IFA_FLAGS);
98
99 if constexpr (std::is_same<TMessage, AddressMessageWithBroadcast<TAddr>>::value)
100 {
101 utils::InitializeAddressAttribute<TAddr>(message.broadcastAddress, broadcastAddress.value(), IFA_BROADCAST);
102 }
103
104 int flags = 0;
105 if (operation == Update)
106 {
107 flags = NLM_F_CREATE | NLM_F_REPLACE;
108 }
109 else if (operation == Create)
110 {
111 flags = NLM_F_CREATE | NLM_F_EXCL;
112 }
113
114 NetlinkChannel channel;
115 auto transaction = channel.CreateTransaction<TMessage>(message, operation == Remove ? RTM_DELADDR : RTM_NEWADDR, flags);
116
117 transaction.Execute();
118 }
119
120 void Interface::ModifyIpAddress(const Address& address, Operation operation)
121 {
122 // NOTE: in-place address update is not supported via NetLink, so remove the address first and
123 // then re-add it. We assume that the caller has saved any other related state beforehand to
124 // restore, such as routes.
125 if (address.IsIpv4())
126 {
127 std::optional<Address> broadcastAddress = utils::ComputeBroadcastAddress(address);
128
129 if (operation == Update)
130 {
131 std::vector<Address> v4Addresses = Ipv4Configuration().Addresses;
132 if (std::find(v4Addresses.begin(), v4Addresses.end(), address) != v4Addresses.end())
133 {
134 ChangeAddress<in_addr>(address, broadcastAddress, Remove);
135 }
136 }
137
138 ChangeAddress<in_addr>(address, broadcastAddress, operation);
139 }
140 else
141 {
142 if (operation == Update)
143 {
144 std::vector<Address> v6Addresses = Ipv6Configuration().Addresses;
145 if (std::find(v6Addresses.begin(), v6Addresses.end(), address) != v6Addresses.end())
146 {
147 ChangeAddress<in6_addr>(address, {}, Remove);
148 }
149 }
150
151 ChangeAddress<in6_addr>(address, {}, operation);
152 }
153 }
154
155 template <size_t wifiTypeArraySize>
156 void Interface::CreateWifiAdapter(const std::string& wifiName, const char (&wifiType)[wifiTypeArraySize])
157 {
158 constexpr int wifiTypeSize = wifiTypeArraySize - 1;
159 struct Request
160 {
161 ifinfomsg Message __attribute__((aligned(NLMSG_ALIGNTO)));
162 utils::IntegerAttribute LinkAttribute;
163 Attribute<Attribute<char[wifiTypeSize]>> LinkInfoAttribute;
164 Attribute<char> NameAttribute;
165 } __attribute__((packed));
166
167 auto buffer = std::vector<char>(RTA_ALIGN(offsetof(Request, NameAttribute.value) + wifiName.size()));
168 auto* request = gslhelpers::get_struct<Request>(gsl::make_span(buffer));
169
170 utils::InitializeIntegerAttribute(request->LinkAttribute, m_index, IFLA_LINK);
171
172 request->LinkInfoAttribute.header.rta_len = RTA_SPACE(sizeof(Attribute<char[wifiTypeSize]>));
173 request->LinkInfoAttribute.header.rta_type = IFLA_LINKINFO;
174 request->LinkInfoAttribute.value.header.rta_len = RTA_SPACE(wifiTypeSize);
175 request->LinkInfoAttribute.value.header.rta_type = IFLA_INFO_KIND;
176 auto typeBuffer = gsl::make_span(buffer).subspan(offsetof(Request, LinkInfoAttribute.value.value));
177 gsl::copy(gsl::make_span(wifiType, wifiTypeSize), typeBuffer);
178
179 request->NameAttribute.header.rta_len = RTA_SPACE(wifiName.size());
180 request->NameAttribute.header.rta_type = IFLA_IFNAME;
181 auto nameBuffer = gsl::make_span(buffer).subspan(offsetof(Request, NameAttribute.value));
182 gsl::copy(gsl::make_span(wifiName), nameBuffer);
183
184 NetlinkChannel channel;
185 auto transaction = channel.CreateTransaction(buffer.data(), buffer.size(), RTM_NEWLINK, NLM_F_CREATE | NLM_F_EXCL);
186 transaction.Execute();
187 }
188
189 void Interface::CreateVirtualWifiAdapter(const std::string& wifiName)
190 {
191 constexpr char virtualWifiType[] = "virt_wifi";
192 CreateWifiAdapter(wifiName, virtualWifiType);
193 }
194
195 void Interface::CreateProxyWifiAdapter(const std::string& wifiName)
196 {
197 constexpr char proxyWifiType[] = "proxy_wifi";
198 CreateWifiAdapter(wifiName, proxyWifiType);
199 }
200
201 void Interface::CreateBondAdapter(const std::string& bondName)
202 {
203 constexpr char bondType[] = "bond";
204 // This corresponds to the command generated by:
205 // ip link add type bond mode active-backup fail_over_mac active
206 // Format appears to be:
207 // uint16_t 0x5
208 // uint16_t option (0x1 = bond mode, 0xd = fail_over_mac)
209 // uint32_t setting (0x1 = active-backup for bond_mode, 0x1 = active for fail_over_mac)
210 constexpr char bondData[] = {0x05, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x05, 0x00, 0x0d, 0x00, 0x01, 0x00, 0x00, 0x00};
211 constexpr int bondTypeSize = sizeof(bondType) - 1;
212 constexpr int bondDataSize = sizeof(bondData);
213 struct LinkInfo
214 {
215 Attribute<char[bondTypeSize]> KindAttribute;
216 Attribute<char[bondDataSize]> DataAttribute;
217 } __attribute__((packed));
218 struct Request
219 {
220 ifinfomsg Message __attribute__((aligned(NLMSG_ALIGNTO)));
221 Attribute<LinkInfo> LinkInfoAttribute;
222 Attribute<char> NameAttribute;
223 } __attribute__((packed));
224
225 auto buffer = std::vector<char>(RTA_ALIGN(offsetof(Request, NameAttribute.value) + bondName.size()));
226 auto* request = gslhelpers::get_struct<Request>(gsl::make_span(buffer));
227
228 request->LinkInfoAttribute.header.rta_len = RTA_SPACE(sizeof(LinkInfo));
229 request->LinkInfoAttribute.header.rta_type = IFLA_LINKINFO;
230
231 request->LinkInfoAttribute.value.KindAttribute.header.rta_len = RTA_SPACE(bondTypeSize);
232 request->LinkInfoAttribute.value.KindAttribute.header.rta_type = IFLA_INFO_KIND;
233 auto typeBuffer = gsl::make_span(buffer).subspan(offsetof(Request, LinkInfoAttribute.value.KindAttribute.value));
234 gsl::copy(gsl::make_span(bondType, bondTypeSize), typeBuffer);
235
236 request->LinkInfoAttribute.value.DataAttribute.header.rta_len = RTA_SPACE(bondDataSize);
237 request->LinkInfoAttribute.value.DataAttribute.header.rta_type = IFLA_INFO_DATA;
238 auto dataBuffer = gsl::make_span(buffer).subspan(offsetof(Request, LinkInfoAttribute.value.DataAttribute.value));
239 gsl::copy(gsl::make_span(bondData, bondDataSize), dataBuffer);
240
241 request->NameAttribute.header.rta_len = RTA_SPACE(bondName.size());
242 request->NameAttribute.header.rta_type = IFLA_IFNAME;
243 auto nameBuffer = gsl::make_span(buffer).subspan(offsetof(Request, NameAttribute.value));
244 gsl::copy(gsl::make_span(bondName), nameBuffer);
245
246 NetlinkChannel channel;
247 auto transaction = channel.CreateTransaction(buffer.data(), buffer.size(), RTM_NEWLINK, NLM_F_CREATE | NLM_F_EXCL);
248 transaction.Execute();
249 }
250
251 void Interface::RemoveFromBond(const Interface& child_interface)
252 {
253 struct Message
254 {
255 ifinfomsg ifinfo;
256 utils::IntegerAttribute master;
257 } __attribute__((packed));
258
259 Message message{};
260 message.ifinfo.ifi_index = child_interface.Index();
261 message.master.header.rta_len = sizeof(message.master);
262 message.master.header.rta_type = IFLA_MASTER;
263 message.master.value = 0;
264
265 NetlinkChannel channel;
266 auto transaction = channel.CreateTransaction(message, RTM_NEWLINK, 0);
267 transaction.Execute([](const NetlinkResponse& response) {
268 fprintf(stderr, "Interface::RemoveFromBond netlink response: %s\n", utils::Stringify(response).c_str());
269 });
270 }
271
272 void Interface::AddToBond(const Interface& child_interface)
273 {
274 struct Message
275 {
276 ifinfomsg ifinfo;
277 utils::IntegerAttribute master;
278 } __attribute__((packed));
279
280 Message message{};
281 message.ifinfo.ifi_index = child_interface.Index();
282 utils::InitializeIntegerAttribute(message.master, m_index, IFLA_MASTER);
283
284 NetlinkChannel channel;
285 auto transaction = channel.CreateTransaction(message, RTM_NEWLINK, 0);
286 transaction.Execute([](const NetlinkResponse& response) {
287 fprintf(stderr, "Interface::AddToBond netlink response: %s\n", utils::Stringify(response).c_str());
288 });
289 }
290
291 void Interface::DeleteInterface()
292 {
293 struct Message
294 {
295 ifinfomsg ifinfo;
296 } __attribute__((packed));
297
298 Message message{};
299 message.ifinfo.ifi_index = m_index;
300
301 NetlinkChannel channel;
302 auto transaction = channel.CreateTransaction(message, RTM_DELLINK, 0);
303 transaction.Execute([](const NetlinkResponse& response) {
304 fprintf(stderr, "Interface::DeleteInterface netlink response: %s\n", utils::Stringify(response).c_str());
305 });
306 }
307
308 void Interface::SetActiveChild(const Interface& child_interface)
309 {
310 constexpr char bondType[] = "bond";
311 constexpr int bondTypeSize = sizeof(bondType) - 1;
312
313 struct ActiveChildInfo
314 {
315 utils::IntegerAttribute ActiveChildAttribute;
316 } __attribute__((packed));
317
318 struct LinkInfo
319 {
320 Attribute<gsl::byte[bondTypeSize]> KindAttribute;
321 Attribute<ActiveChildInfo> DataAttribute;
322 } __attribute__((packed));
323
324 struct Request
325 {
326 ifinfomsg Message __attribute__((aligned(NLMSG_ALIGNTO)));
327 Attribute<LinkInfo> LinkInfoAttribute;
328 } __attribute__((packed));
329
330 auto buffer = std::vector<gsl::byte>(RTA_ALIGN(sizeof(Request)));
331 auto* request = gslhelpers::get_struct<Request>(gsl::make_span(buffer));
332
333 request->Message.ifi_index = m_index;
334
335 request->LinkInfoAttribute.header.rta_len = sizeof(request->LinkInfoAttribute);
336 request->LinkInfoAttribute.header.rta_type = IFLA_LINKINFO;
337
338 request->LinkInfoAttribute.value.KindAttribute.header.rta_len = sizeof(request->LinkInfoAttribute.value.KindAttribute);
339 request->LinkInfoAttribute.value.KindAttribute.header.rta_type = IFLA_INFO_KIND;
340 auto typeBuffer = gsl::make_span(buffer).subspan(offsetof(Request, LinkInfoAttribute.value.KindAttribute.value));
341 gsl::copy(gsl::as_bytes(gsl::make_span(bondType, bondTypeSize)), typeBuffer);
342
343 request->LinkInfoAttribute.value.DataAttribute.header.rta_len = sizeof(request->LinkInfoAttribute.value.DataAttribute);
344 request->LinkInfoAttribute.value.DataAttribute.header.rta_type = IFLA_INFO_DATA;
345
346 utils::InitializeIntegerAttribute(
347 request->LinkInfoAttribute.value.DataAttribute.value.ActiveChildAttribute, child_interface.Index(), IFLA_BOND_ACTIVE_SLAVE);
348
349 NetlinkChannel channel;
350 auto transaction = channel.CreateTransaction(buffer.data(), buffer.size(), RTM_NEWLINK, 0);
351 transaction.Execute([](const NetlinkResponse& response) {
352 fprintf(stderr, "Interface::SetActiveChild(bond) netlink response: %s\n", utils::Stringify(response).c_str());
353 });
354 }
355
356 void Interface::CreateTunTapAdapter(const std::string& name, bool TunAdapter)
357 {
358 wil::unique_fd fd;
359 if (name.size() >= IFNAMSIZ)
360 {
361 throw RuntimeErrorWithSourceLocation("Tun adapter name exceeds IFNAMSIZ");
362 }
363
364 ifreq ifr{};
365 ifr.ifr_flags = TunAdapter ? IFF_TUN : IFF_TAP;
366 fd = Syscall(open, "/dev/net/tun", O_RDWR);
367 std::copy(name.begin(), name.end(), ifr.ifr_name);
368 Syscall(ioctl, fd.get(), TUNSETIFF, &ifr);
369 Syscall(ioctl, fd.get(), TUNSETPERSIST, 1);
370 }
371
372 void Interface::CreateTunAdapter(const std::string& name)
373 {
374 CreateTunTapAdapter(name, true);
375 }
376
377 void Interface::CreateTapAdapter(const std::string& name)
378 {
379 CreateTunTapAdapter(name, false);
380 }
381
382 void Interface::SetMtu(int mtu)
383 {
384 struct Message
385 {
386 ifinfomsg ifinfo;
387 utils::IntegerAttribute mtu;
388 } __attribute__((packed));
389
390 Message message{};
391 message.ifinfo.ifi_index = m_index;
392 utils::InitializeIntegerAttribute(message.mtu, mtu, IFLA_MTU);
393
394 NetlinkChannel channel;
395 auto transaction = channel.CreateTransaction(message, RTM_NEWLINK, 0);
396 transaction.Execute();
397 }
398
399 void Interface::SetMetric(int metric)
400 {
401 struct Message
402 {
403 ifinfomsg ifinfo;
404 utils::IntegerAttribute metric;
405 } __attribute__((packed));
406
407 Message message{};
408 message.ifinfo.ifi_index = m_index;
409 utils::InitializeIntegerAttribute(message.metric, metric, IFLA_PRIORITY);
410
411 NetlinkChannel channel;
412 auto transaction = channel.CreateTransaction(message, RTM_NEWLINK, 0);
413 transaction.Execute();
414 }
415
416 void Interface::SetIpv4Configuration(const InterfaceConfiguration& configuration)
417 {
418 SetConfiguration<in_addr>(Ipv4Configuration(), configuration);
419 }
420
421 void Interface::SetIpv6Configuration(const InterfaceConfiguration& configuration)
422 {
423 SetConfiguration<in6_addr>(Ipv6Configuration(), configuration);
424 }
425
426 void Interface::SetName(const std::string& newName)
427 {
428 struct Request
429 {
430 struct ifinfomsg Message __attribute__((aligned(NLMSG_ALIGNTO)));
431 Attribute<char> Attribute;
432 };
433
434 auto buffer = std::vector<char>(RTA_ALIGN(offsetof(Request, Attribute.value) + newName.size()));
435 auto* request = gslhelpers::get_struct<Request>(gsl::make_span(buffer));
436 request->Message.ifi_index = m_index;
437 request->Attribute.header.rta_len = RTA_LENGTH(newName.size());
438 request->Attribute.header.rta_type = IFLA_IFNAME;
439 auto nameBuffer = gsl::make_span(buffer).subspan(offsetof(Request, Attribute.value));
440 gsl::copy(gsl::make_span(newName), nameBuffer);
441
442 NetlinkChannel channel;
443 auto transaction = channel.CreateTransaction(buffer.data(), buffer.size(), RTM_NEWLINK, 0);
444 transaction.Execute();
445 }
446
447 void Interface::SetWiphyNamespace(int namespaceFd)
448 {
449 struct Request
450 {
451 int Command;
452 utils::IntegerAttribute Wiphy;
453 utils::IntegerAttribute NamespaceFd;
454 };
455
456 Request request{};
457 request.Command = NL80211_CMD_SET_WIPHY_NETNS;
458
459 utils::InitializeIntegerAttribute(request.Wiphy, 0, NL80211_ATTR_WIPHY);
460 utils::InitializeIntegerAttribute(request.NamespaceFd, namespaceFd, NL80211_ATTR_NETNS_FD);
461
462 NetlinkChannel channel(SOCK_RAW, NETLINK_GENERIC);
463 auto transaction = channel.CreateTransaction(request, 0x15, 0);
464 transaction.Execute();
465 }
466
467 void Interface::SetNamespace(int namespaceFd)
468 {
469 struct Request
470 {
471 struct ifinfomsg Message __attribute__((aligned(NLMSG_ALIGNTO)));
472 utils::IntegerAttribute Attribute;
473 };
474
475 Request request{};
476 request.Message.ifi_index = m_index;
477
478 utils::InitializeIntegerAttribute(request.Attribute, namespaceFd, IFLA_NET_NS_FD);
479
480 NetlinkChannel channel;
481 auto transaction = channel.CreateTransaction<Request>(request, RTM_NEWLINK, 0);
482 transaction.Execute();
483 }
484
485 void Interface::AddFlags(int flags)
486 {
487 NetlinkChannel channel;
488 const auto currentFlags = channel.GetInterfaceFlags(m_name);
489
490 channel.SetInterfaceFlags(m_name, currentFlags | flags);
491 }
492
493 void Interface::RemoveFlags(int flags)
494 {
495 NetlinkChannel channel;
496 const auto currentFlags = channel.GetInterfaceFlags(m_name);
497
498 channel.SetInterfaceFlags(m_name, currentFlags & ~flags);
499 }
500
501 void Interface::SetUp()
502 {
503 AddFlags(IFF_UP | IFF_RUNNING);
504 }
505
506 void Interface::SetDown()
507 {
508 RemoveFlags(IFF_UP | IFF_RUNNING);
509 }
510
511 void Interface::SetMacAddress(const MacAddress& address)
512 {
513 static_assert(sizeof(address) == 6);
514
515 struct Message
516 {
517 ifinfomsg ifinfo;
518 utils::MacAddressAttribute address;
519 } __attribute__((packed));
520
521 Message message{};
522 message.ifinfo.ifi_index = m_index;
523 message.address.header.nla_len = sizeof(message.address);
524 message.address.header.nla_type = IFLA_ADDRESS;
525 message.address.address = address;
526
527 NetlinkChannel channel;
528 auto transaction = channel.CreateTransaction(message, RTM_NEWLINK, 0);
529 transaction.Execute();
530 }
531
532 MacAddress Interface::GetMacAddress()
533 {
534 MacAddress address;
535 ifreq interface_request{};
536 wil::unique_fd fd;
537 fd = Syscall(socket, AF_PACKET, SOCK_RAW, htons(ETH_P_ALL));
538 std::copy(m_name.begin(), m_name.end(), interface_request.ifr_ifrn.ifrn_name);
539 Syscall(ioctl, fd.get(), SIOCGIFHWADDR, &interface_request);
540 std::copy(
541 interface_request.ifr_ifru.ifru_hwaddr.sa_data, interface_request.ifr_ifru.ifru_hwaddr.sa_data + address.size(), address.begin());
542 return address;
543 }
544
545 template <typename TAddr>
546 void Interface::SetConfiguration(const InterfaceConfiguration& currentConfiguration, const InterfaceConfiguration& configuration)
547 {
548 std::vector<const Address*> add;
549 std::vector<const Address*> remove;
550
551 auto compare = [](const std::vector<Address>& left, const std::vector<Address>& right, std::vector<const Address*>& dest) {
552 for (const auto& e : left)
553 {
554 if (std::find(right.begin(), right.end(), e) == right.end())
555 {
556 dest.emplace_back(&e);
557 }
558 }
559 };
560
561 compare(currentConfiguration.Addresses, configuration.Addresses, remove);
562 compare(configuration.Addresses, currentConfiguration.Addresses, add);
563
564 for (const auto* address : remove)
565 {
566 ChangeAddress<TAddr>(*address, configuration.BroadcastAddress, Remove);
567 }
568
569 for (const auto* address : add)
570 {
571 ChangeAddress<TAddr>(*address, configuration.BroadcastAddress, Create);
572 }
573 }
574
575 InterfaceConfiguration Interface::Ipv6Configuration()
576 {
577 return ListAddressesImpl(AF_INET6);
578 }
579
580 InterfaceConfiguration Interface::Ipv4Configuration()
581 {
582 return ListAddressesImpl(AF_INET);
583 }
584
585 InterfaceConfiguration Interface::ListAddressesImpl(int af)
586 {
587 InterfaceConfiguration configuration;
588 auto routine = [&](const NetlinkResponse& response) {
589 auto messages = response.Messages<ifaddrmsg>(RTM_NEWADDR);
590
591 for (const auto& message : messages)
592 {
593 const auto* ifaddr = message.Payload();
594 if (ifaddr->ifa_index != static_cast<size_t>(m_index))
595 {
596 continue;
597 }
598
599 auto getAddresses = [&](int type, std::vector<Address>& dest) {
600 for (const auto attribute : message.Attributes<const void*>(type))
601 {
602 auto messageAf = message.Payload()->ifa_family;
603 int prefixLength = message.Payload()->ifa_prefixlen;
604 dest.emplace_back(Address::FromBinary(messageAf, prefixLength, attribute));
605 }
606 };
607
608 getAddresses(IFA_ADDRESS, configuration.Addresses);
609 getAddresses(IFA_LOCAL, configuration.LocalAddresses);
610
611 std::vector<Address> broadcastAddresses;
612 getAddresses(IFA_BROADCAST, broadcastAddresses);
613 if (broadcastAddresses.size() == 1)
614 {
615 configuration.BroadcastAddress = std::move(broadcastAddresses[0]);
616 }
617 else if (broadcastAddresses.size() > 1)
618 {
619 throw RuntimeErrorWithSourceLocation("More than one broadcast address found");
620 }
621 }
622 };
623
624 NetlinkChannel channel;
625
626 ifaddrmsg payload = {};
627 payload.ifa_family = af;
628 payload.ifa_index = m_index;
629 auto transaction = channel.CreateTransaction(payload, RTM_GETADDR, NLM_F_DUMP);
630
631 transaction.Execute(routine);
632
633 return configuration;
634 }
635
636 /*
637 Implements functionality equivalent to sysctl -w net.ipv(4/6).conf.<interface>.<setting>, by writing to
638 /proc/sys/net/ipv(4/6)/conf/m_name
639 */
640 void Interface::EnableNetworkSetting(const char* settingName, int addressFamily)
641 {
642 const std::string settingFilePath =
643 std::format("/proc/sys/net/{}/conf/{}/{}", (addressFamily == AF_INET ? "ipv4" : "ipv6"), m_name, settingName);
644
645 wil::unique_fd fd(Syscall(open, settingFilePath.c_str(), (O_WRONLY | O_CLOEXEC)));
646
647 Syscall(write, fd.get(), c_value1, sizeof(c_value1) - 1);
648 }
649
650 void Interface::DisableNetworkSetting(const char* settingName, int addressFamily)
651 {
652 const std::string settingFilePath =
653 std::format("/proc/sys/net/{}/conf/{}/{}", (addressFamily == AF_INET ? "ipv4" : "ipv6"), m_name, settingName);
654
655 wil::unique_fd fd(Syscall(open, settingFilePath.c_str(), (O_WRONLY | O_CLOEXEC)));
656
657 Syscall(write, fd.get(), c_value0, sizeof(c_value0) - 1);
658 }
659
660 void Interface::ResetIpv6State()
661 {
662 // Disable IPv6, turn off address autogeneration and router discovery, and then re-enable IPv6.
663 // Some process (presumably netd) re-enables some of these settings after receiving new
664 // adapter configuration (eg, after connecting to a new Wi-Fi network), so this function should
665 // be called after that to restore the settings to the desired values.
666 EnableNetworkSetting("disable_ipv6", AF_INET6);
667
668 DisableNetworkSetting("accept_ra", AF_INET6);
669 DisableNetworkSetting("autoconf", AF_INET6);
670 DisableNetworkSetting("use_tempaddr", AF_INET6);
671 EnableNetworkSetting("addr_gen_mode", AF_INET6); // A value of 1 means address generation is disabled.
672
673 DisableNetworkSetting("disable_ipv6", AF_INET6);
674 }
675
676 int Interface::Index() const
677 {
678 return m_index;
679 }
680
681 const std::string& Interface::Name() const
682 {
683 return m_name;
684 }
685
686 Interface Interface::Open(const std::string& name)
687 {
688 NetlinkChannel channel;
689 return {channel.GetInterfaceIndex(name), name};
690 }
691
692 void Interface::ModifyTcClassifier(bool Add)
693 {
694 char kind[] = "clsact";
695 struct Message
696 {
697 tcmsg TcMsg;
698 Attribute<char[sizeof(kind)]> Kind;
699 } __attribute__((packed));
700 Message message{};
701
702 message.TcMsg.tcm_family = AF_UNSPEC;
703 message.TcMsg.tcm_ifindex = m_index;
704 message.TcMsg.tcm_handle = TC_H_MAKE(TC_H_CLSACT, 0);
705 message.TcMsg.tcm_parent = TC_H_CLSACT;
706 message.TcMsg.tcm_info = 0;
707 message.Kind.header.rta_len = sizeof(kind) + sizeof(rtattr);
708 message.Kind.header.rta_type = TCA_KIND;
709 std::copy(kind, kind + sizeof(kind), message.Kind.value);
710
711 NetlinkChannel channel;
712 auto transaction =
713 channel.CreateTransaction(message, Add ? RTM_NEWQDISC : RTM_DELQDISC, NLM_F_REQUEST | (Add ? (NLM_F_CREATE | NLM_F_EXCL) : 0));
714 transaction.Execute();
715 }
716
717 void Interface::BpfAttachTcClassifier(int ProgramFd, bool Ingress)
718 {
719 char name[] = "gns";
720 struct TcOptions
721 {
722 Attribute<int> Fd;
723 Attribute<char[sizeof(name)]> Name;
724 Attribute<int> Flags;
725 } __attribute__((packed));
726 char kind[] = "bpf";
727 struct Message
728 {
729 tcmsg TcMsg;
730 Attribute<char[sizeof(kind)]> Kind;
731 Attribute<TcOptions> TcOptions;
732 } __attribute__((packed));
733 Message message{};
734
735 message.TcMsg.tcm_family = AF_UNSPEC;
736 message.TcMsg.tcm_ifindex = m_index;
737 message.TcMsg.tcm_handle = 0;
738 message.TcMsg.tcm_parent = TC_H_MAKE(TC_H_CLSACT, Ingress ? TC_H_MIN_INGRESS : TC_H_MIN_EGRESS);
739 message.TcMsg.tcm_info = htons(ETH_P_ALL);
740 message.Kind.header.rta_len = sizeof(kind) + sizeof(rtattr);
741 message.Kind.header.rta_type = TCA_KIND;
742 std::copy(kind, kind + sizeof(kind), message.Kind.value);
743 message.TcOptions.header.rta_len = sizeof(TcOptions) + sizeof(rtattr);
744 message.TcOptions.header.rta_type = TCA_OPTIONS;
745 message.TcOptions.value.Fd.header.rta_len = sizeof(int) + sizeof(rtattr);
746 message.TcOptions.value.Fd.header.rta_type = TCA_BPF_FD;
747 message.TcOptions.value.Fd.value = ProgramFd;
748 message.TcOptions.value.Name.header.rta_len = sizeof(name) + sizeof(rtattr);
749 message.TcOptions.value.Name.header.rta_type = TCA_BPF_NAME;
750 message.TcOptions.value.Flags.header.rta_len = sizeof(int) + sizeof(rtattr);
751 message.TcOptions.value.Flags.header.rta_type = TCA_BPF_FLAGS;
752 message.TcOptions.value.Flags.value = TCA_BPF_FLAG_ACT_DIRECT;
753 std::copy(name, name + sizeof(name), message.TcOptions.value.Name.value);
754
755 NetlinkChannel channel;
756 auto transaction = channel.CreateTransaction(message, RTM_NEWTFILTER, NLM_F_REQUEST | NLM_F_EXCL | NLM_F_CREATE);
757 transaction.Execute();
758 }