master
cpp 299 lines 11 KB
Raw
1 // Copyright (C) Microsoft Corporation. All rights reserved.
2 #include "RuntimeErrorWithSourceLocation.h"
3 #include "IpRuleManager.h"
4 #include "NetlinkTransactionError.h"
5 #include "Utils.h"
6 #include "common.h"
7
8 struct LoopbackInterfaceAttribute
9 {
10 rtattr header;
11 // A buffer of size 4 is used for alignment purposes (aligning to NLA_ALIGNTO).
12 // The loopback interface name is hardcoded as 'lo'.
13 char interface[4] = {'l', 'o', '\0', '\0'};
14 } __attribute__((packed));
15
16 template <DerivedRuleMessage TMessage>
17 void IpRuleManager::SendMessage(unsigned char family, int routingTable, int operation, int flags, const std::function<void(TMessage&)>& routine)
18 {
19 TMessage message{};
20 message.rule.rtm_family = family;
21 message.rule.rtm_table = 0;
22 message.rule.rtm_protocol = RTPROT_BOOT;
23 message.rule.rtm_type = RTN_UNICAST;
24 message.rule.rtm_scope = RT_SCOPE_UNIVERSE;
25
26 utils::InitializeIntegerAttribute(message.tableId, routingTable, FRA_TABLE);
27
28 routine(message);
29
30 auto transaction = m_channel.CreateTransaction(message, operation, flags);
31 try
32 {
33 transaction.Execute();
34 }
35 catch (const NetlinkTransactionError& transactionErr)
36 {
37 auto errorCode = transactionErr.Error();
38 if (errorCode.has_value())
39 {
40 // Errors "file exists" and "file not found" are ignored in order to avoid keeping
41 // track in GnsDaemon of what rules were added/deleted and allow the same rule
42 // to be added/deleted multiple times.
43 if (errorCode.value() == -EEXIST || errorCode.value() == -ENOENT)
44 {
45 return;
46 }
47 }
48
49 throw;
50 }
51 }
52
53 void IpRuleManager::ModifyLoopbackRule(const Rule& rule, Operation operation)
54 {
55 if (!rule.protocol.has_value())
56 {
57 throw RuntimeErrorWithSourceLocation("Loopback rule missing protocol");
58 }
59 if (operation != Operation::Create && operation != Operation::Remove)
60 {
61 throw RuntimeErrorWithSourceLocation(std::format("Unexpected operation: {}", static_cast<int>(operation)));
62 }
63 if (rule.iif.empty())
64 {
65 throw RuntimeErrorWithSourceLocation("Loopback rule has empty iif name");
66 }
67
68 GNS_LOG_INFO("{} rule {}", operation == Operation::Create ? "Add" : "Remove", utils::Stringify(rule).c_str());
69
70 struct Message
71 {
72 rtmsg rule;
73 utils::IntegerAttribute tableId;
74 utils::IntegerAttribute priority;
75 utils::Attribute<uint8_t> protocol;
76 utils::Attribute<char> iifName;
77 } __attribute__((packed));
78
79 // In case of Remove, there are no additional flags needed besides NLM_F_REQUEST | NLM_F_ACK.
80 int flags = 0;
81 if (operation == Create)
82 {
83 flags = NLM_F_CREATE;
84 }
85
86 int netlinkOperation = operation == Remove ? RTM_DELRULE : RTM_NEWRULE;
87
88 auto buffer = std::vector<char>(RTA_ALIGN(offsetof(Message, iifName.value) + rule.iif.size()));
89 auto* message = gslhelpers::get_struct<Message>(gsl::make_span(buffer));
90
91 message->rule.rtm_family = rule.family;
92 message->rule.rtm_table = 0;
93 message->rule.rtm_protocol = RTPROT_BOOT;
94 message->rule.rtm_type = RTN_UNICAST;
95 message->rule.rtm_scope = RT_SCOPE_UNIVERSE;
96
97 utils::InitializeIntegerAttribute(message->tableId, rule.routingTable, FRA_TABLE);
98 utils::InitializeIntegerAttribute(message->priority, rule.priority, RTA_PRIORITY);
99
100 message->protocol.header.rta_len = RTA_LENGTH(sizeof(uint8_t));
101 message->protocol.header.rta_type = FRA_IP_PROTO;
102 message->protocol.value = rule.protocol.value() == Tcp ? IPPROTO_TCP : IPPROTO_UDP;
103
104 message->iifName.header.rta_len = RTA_SPACE(rule.iif.size());
105 message->iifName.header.rta_type = FRA_IIFNAME;
106 auto iifNameBuffer = gsl::make_span(buffer).subspan(offsetof(Message, iifName.value));
107 gsl::copy(gsl::make_span(rule.iif), iifNameBuffer);
108
109 // The SendMessage helper cannot be used here because we are sending a variable sized message
110 // (the message contains the iifName field with variable length)
111 auto transaction = m_channel.CreateTransaction(buffer.data(), buffer.size(), netlinkOperation, flags);
112 try
113 {
114 transaction.Execute();
115 }
116 catch (const NetlinkTransactionError& transactionErr)
117 {
118 auto errorCode = transactionErr.Error();
119 if (errorCode.has_value())
120 {
121 // Errors "file exists" and "file not found" are ignored in order to avoid keeping
122 // track in GnsDaemon of what rules were added/deleted and allow the same rule
123 // to be added/deleted multiple times.
124 if (errorCode.value() == -EEXIST || errorCode.value() == -ENOENT)
125 {
126 return;
127 }
128 }
129
130 throw;
131 }
132 }
133
134 void IpRuleManager::ModifyLoopbackRuleWithSourceAddress(const Rule& rule, Operation action)
135 {
136 if (rule.family != AF_INET && rule.family != AF_INET6)
137 {
138 throw RuntimeErrorWithSourceLocation(std::format("Unexpected address family: {}", rule.family));
139 }
140 if (action != Operation::Create && action != Operation::Remove)
141 {
142 throw RuntimeErrorWithSourceLocation(std::format("Unexpected operation: {}", static_cast<int>(action)));
143 }
144
145 if (rule.family == AF_INET)
146 {
147 ModifyLoopbackRuleWithSourceAddressImpl<in_addr>(rule, action);
148 }
149 else
150 {
151 ModifyLoopbackRuleWithSourceAddressImpl<in6_addr>(rule, action);
152 }
153 }
154
155 template <typename TAddr>
156 void IpRuleManager::ModifyLoopbackRuleWithSourceAddressImpl(const Rule& rule, Operation operation)
157 {
158 if (!rule.protocol.has_value())
159 {
160 throw RuntimeErrorWithSourceLocation("Rule missing protocol");
161 }
162 if (!rule.sourceAddress.has_value())
163 {
164 throw RuntimeErrorWithSourceLocation("Rule missing source IP");
165 }
166
167 GNS_LOG_INFO("{} rule {}", operation == Operation::Create ? "Add" : "Remove", utils::Stringify(rule).c_str());
168
169 int flags = (operation == Create) ? NLM_F_CREATE : 0;
170
171 struct Message : RuleMessage
172 {
173 LoopbackInterfaceAttribute devName;
174 utils::AddressAttribute<TAddr> from;
175 utils::IntegerAttribute priority;
176 utils::Attribute<uint8_t> protocol;
177 } __attribute__((packed));
178
179 int netlinkOperation = operation == Remove ? RTM_DELRULE : RTM_NEWRULE;
180
181 SendMessage<Message>(rule.family, rule.routingTable, netlinkOperation, flags, [&](Message& message) {
182 message.devName.header.rta_len = sizeof(message.devName);
183 message.devName.header.rta_type = FRA_IIFNAME;
184
185 // Set source address in the rule
186 message.rule.rtm_src_len = rule.sourceAddress->PrefixLength();
187 utils::InitializeAddressAttribute<TAddr>(message.from, rule.sourceAddress.value(), FRA_SRC);
188
189 utils::InitializeIntegerAttribute(message.priority, rule.priority, RTA_PRIORITY);
190
191 message.protocol.header.rta_len = RTA_LENGTH(sizeof(uint8_t));
192 message.protocol.header.rta_type = FRA_IP_PROTO;
193 message.protocol.value = rule.protocol.value() == Tcp ? IPPROTO_TCP : IPPROTO_UDP;
194 });
195 }
196
197 void IpRuleManager::ModifyRoutingTablePriority(const Rule& rule, Operation operation)
198 {
199 if (operation != Operation::Create && operation != Operation::Remove)
200 {
201 throw RuntimeErrorWithSourceLocation(std::format("Unexpected operation: {}", static_cast<int>(operation)));
202 }
203
204 GNS_LOG_INFO("{} rule {}", operation == Operation::Create ? "Add" : "Remove", utils::Stringify(rule));
205
206 struct Message : RuleMessage
207 {
208 utils::IntegerAttribute priority;
209 } __attribute__((packed));
210
211 // In case of Remove, there are no additional flags needed besides NLM_F_REQUEST | NLM_F_ACK.
212 int flags = 0;
213 if (operation == Create)
214 {
215 flags = NLM_F_CREATE;
216 }
217
218 int netlinkOperation = operation == Remove ? RTM_DELRULE : RTM_NEWRULE;
219
220 SendMessage<Message>(rule.family, rule.routingTable, netlinkOperation, flags, [&](Message& message) {
221 utils::InitializeIntegerAttribute(message.priority, rule.priority, RTA_PRIORITY);
222 });
223 }
224
225 void IpRuleManager::ModifyRoutingTablePriorityWithProtocol(const Rule& rule, Operation operation)
226 {
227 if (operation != Operation::Create && operation != Operation::Remove)
228 {
229 throw RuntimeErrorWithSourceLocation(std::format("Unexpected operation: {}", static_cast<int>(operation)));
230 }
231 if (!rule.protocol.has_value())
232 {
233 throw RuntimeErrorWithSourceLocation("Rule missing protocol");
234 }
235
236 GNS_LOG_INFO("{} rule {}", operation == Operation::Create ? "Add" : "Remove", utils::Stringify(rule));
237
238 struct Message : RuleMessage
239 {
240 utils::IntegerAttribute priority;
241 utils::Attribute<uint8_t> protocol;
242 } __attribute__((packed));
243
244 // In case of Remove, there are no additional flags needed besides NLM_F_REQUEST | NLM_F_ACK.
245 int flags = 0;
246 if (operation == Create)
247 {
248 flags = NLM_F_CREATE;
249 }
250
251 int netlinkOperation = operation == Remove ? RTM_DELRULE : RTM_NEWRULE;
252
253 SendMessage<Message>(rule.family, rule.routingTable, netlinkOperation, flags, [&](Message& message) {
254 utils::InitializeIntegerAttribute(message.priority, rule.priority, RTA_PRIORITY);
255
256 message.protocol.header.rta_len = RTA_LENGTH(sizeof(uint8_t));
257 message.protocol.header.rta_type = FRA_IP_PROTO;
258 message.protocol.value = rule.protocol.value() == Tcp ? IPPROTO_TCP : IPPROTO_UDP;
259 });
260 }
261
262 std::vector<Rule> IpRuleManager::ListRules(int family, int tableId)
263 {
264 struct Message
265 {
266 rtmsg rule;
267 } __attribute__((packed));
268
269 std::vector<Rule> rules{};
270
271 auto onMessage = [&](const NetlinkResponse& response) {
272 for (const auto& e : response.Messages<rtmsg>(RTM_NEWRULE))
273 {
274 const auto msg = e.Payload();
275 const auto priorityAttr = e.UniqueAttribute<int>(FRA_PRIORITY);
276 const auto oifAttr = e.UniqueAttribute<char>(FRA_OIFNAME);
277 const auto protocolAttr = e.UniqueAttribute<uint8_t>(FRA_IP_PROTO);
278 const auto tableAttr = e.UniqueAttribute<int>(FRA_TABLE);
279
280 int priority = priorityAttr.has_value() ? *priorityAttr.value() : -1;
281 std::string oif(oifAttr.value_or(""));
282 uint8_t proto = protocolAttr.has_value() ? *protocolAttr.value() : -1;
283 std::optional<Protocol> protocol =
284 (proto == IPPROTO_TCP) ? std::optional(Tcp) : ((proto == IPPROTO_UDP) ? std::optional(Udp) : std::optional<Protocol>());
285 int tableAttrId = tableAttr.has_value() ? *tableAttr.value() : msg->rtm_table;
286
287 rules.emplace_back(Rule(msg->rtm_family, tableAttrId, priority, oif, protocol));
288 }
289 };
290
291 rtmsg message{};
292 message.rtm_family = family;
293 message.rtm_table = 0;
294
295 auto transaction = m_channel.CreateTransaction(message, RTM_GETRULE, NLM_F_DUMP);
296 transaction.Execute(onMessage);
297
298 return rules;
299 }