]>
Commit | Line | Data |
---|---|---|
a61dd3f3 Y |
1 | /* |
2 | * This file is part of PowerDNS or dnsdist. | |
3 | * Copyright -- PowerDNS.COM B.V. and its contributors | |
4 | * | |
5 | * This program is free software; you can redistribute it and/or modify | |
6 | * it under the terms of version 2 of the GNU General Public License as | |
7 | * published by the Free Software Foundation. | |
8 | * | |
9 | * In addition, for the avoidance of any doubt, permission is granted to | |
10 | * link this program with OpenSSL and to (re)distribute the binaries | |
11 | * produced as the result of such linking. | |
12 | * | |
13 | * This program is distributed in the hope that it will be useful, | |
14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
16 | * GNU General Public License for more details. | |
17 | * | |
18 | * You should have received a copy of the GNU General Public License | |
19 | * along with this program; if not, write to the Free Software | |
20 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. | |
21 | */ | |
b1dd62ee RG |
22 | |
23 | #include "config.h" | |
24 | ||
25 | #ifdef HAVE_XSK | |
26 | ||
a61dd3f3 | 27 | #include <algorithm> |
a61dd3f3 Y |
28 | #include <cstdint> |
29 | #include <cstring> | |
30 | #include <fcntl.h> | |
31 | #include <iterator> | |
32 | #include <linux/bpf.h> | |
e3044aca | 33 | #include <linux/if_ether.h> |
a61dd3f3 Y |
34 | #include <linux/if_link.h> |
35 | #include <linux/if_xdp.h> | |
36 | #include <linux/ip.h> | |
37 | #include <linux/ipv6.h> | |
38 | #include <linux/tcp.h> | |
e3044aca RG |
39 | #include <linux/types.h> |
40 | #include <linux/udp.h> | |
a61dd3f3 | 41 | #include <net/if.h> |
ce2e1328 | 42 | #include <net/if_arp.h> |
a61dd3f3 Y |
43 | #include <netinet/in.h> |
44 | #include <poll.h> | |
45 | #include <stdexcept> | |
46 | #include <sys/eventfd.h> | |
47 | #include <sys/ioctl.h> | |
48 | #include <sys/mman.h> | |
49 | #include <sys/socket.h> | |
50 | #include <sys/timerfd.h> | |
51 | #include <unistd.h> | |
52 | #include <vector> | |
53 | ||
a61dd3f3 Y |
54 | #include <bpf/bpf.h> |
55 | #include <bpf/libbpf.h> | |
56 | extern "C" | |
57 | { | |
58 | #include <xdp/libxdp.h> | |
59 | } | |
60 | ||
677f9d2b RG |
61 | #include "gettime.hh" |
62 | #include "xsk.hh" | |
63 | ||
677f9d2b | 64 | #ifdef DEBUG_UMEM |
cce416f2 RG |
65 | namespace |
66 | { | |
677f9d2b RG |
67 | struct UmemEntryStatus |
68 | { | |
cce416f2 RG |
69 | enum class Status : uint8_t |
70 | { | |
71 | Free, | |
72 | FillQueue, | |
73 | Received, | |
74 | TXQueue | |
75 | }; | |
677f9d2b RG |
76 | Status status{Status::Free}; |
77 | }; | |
78 | ||
79 | LockGuarded<std::unordered_map<uint64_t, UmemEntryStatus>> s_umems; | |
80 | ||
81 | void checkUmemIntegrity(const char* function, int line, uint64_t offset, const std::set<UmemEntryStatus::Status>& validStatuses, UmemEntryStatus::Status newStatus) | |
82 | { | |
83 | auto umems = s_umems.lock(); | |
84 | if (validStatuses.count(umems->at(offset).status) == 0) { | |
85 | std::cerr << "UMEM integrity check failed at " << function << ": " << line << ": status is " << static_cast<int>(umems->at(offset).status) << ", expected: "; | |
86 | for (const auto status : validStatuses) { | |
87 | std::cerr << static_cast<int>(status) << " "; | |
88 | } | |
89 | std::cerr << std::endl; | |
90 | abort(); | |
91 | } | |
92 | (*umems)[offset].status = newStatus; | |
93 | } | |
94 | } | |
95 | #endif /* DEBUG_UMEM */ | |
96 | ||
a61dd3f3 Y |
97 | constexpr bool XskSocket::isPowOfTwo(uint32_t value) noexcept |
98 | { | |
99 | return value != 0 && (value & (value - 1)) == 0; | |
100 | } | |
4dd6b4b4 | 101 | |
a61dd3f3 Y |
102 | int XskSocket::firstTimeout() |
103 | { | |
104 | if (waitForDelay.empty()) { | |
105 | return -1; | |
106 | } | |
2a5e9760 | 107 | timespec now{}; |
a61dd3f3 | 108 | gettime(&now); |
ae61f00a | 109 | const auto& firstTime = waitForDelay.top().getSendTime(); |
a61dd3f3 Y |
110 | const auto res = timeDifference(now, firstTime); |
111 | if (res <= 0) { | |
112 | return 0; | |
113 | } | |
114 | return res; | |
115 | } | |
677f9d2b | 116 | |
2a5e9760 RG |
117 | XskSocket::XskSocket(size_t frameNum_, std::string ifName_, uint32_t queue_id, const std::string& xskMapPath) : |
118 | frameNum(frameNum_), ifName(std::move(ifName_)), socket(nullptr, xsk_socket__delete), sharedEmptyFrameOffset(std::make_shared<LockGuarded<vector<uint64_t>>>()) | |
a61dd3f3 Y |
119 | { |
120 | if (!isPowOfTwo(frameNum_) || !isPowOfTwo(frameSize) | |
121 | || !isPowOfTwo(fqCapacity) || !isPowOfTwo(cqCapacity) || !isPowOfTwo(rxCapacity) || !isPowOfTwo(txCapacity)) { | |
122 | throw std::runtime_error("The number of frame , the size of frame and the capacity of rings must is a pow of 2"); | |
123 | } | |
124 | getMACFromIfName(); | |
125 | ||
126 | memset(&cq, 0, sizeof(cq)); | |
127 | memset(&fq, 0, sizeof(fq)); | |
128 | memset(&tx, 0, sizeof(tx)); | |
129 | memset(&rx, 0, sizeof(rx)); | |
677f9d2b | 130 | |
2a5e9760 | 131 | xsk_umem_config umemCfg{}; |
a61dd3f3 Y |
132 | umemCfg.fill_size = fqCapacity; |
133 | umemCfg.comp_size = cqCapacity; | |
134 | umemCfg.frame_size = frameSize; | |
135 | umemCfg.frame_headroom = XSK_UMEM__DEFAULT_FRAME_HEADROOM; | |
136 | umemCfg.flags = 0; | |
137 | umem.umemInit(frameNum_ * frameSize, &cq, &fq, &umemCfg); | |
677f9d2b | 138 | |
a61dd3f3 | 139 | { |
2a5e9760 | 140 | xsk_socket_config socketCfg{}; |
a61dd3f3 Y |
141 | socketCfg.rx_size = rxCapacity; |
142 | socketCfg.tx_size = txCapacity; | |
143 | socketCfg.bind_flags = XDP_USE_NEED_WAKEUP; | |
144 | socketCfg.xdp_flags = XDP_FLAGS_SKB_MODE; | |
145 | socketCfg.libxdp_flags = XSK_LIBBPF_FLAGS__INHIBIT_PROG_LOAD; | |
146 | xsk_socket* tmp = nullptr; | |
147 | auto ret = xsk_socket__create(&tmp, ifName.c_str(), queue_id, umem.umem, &rx, &tx, &socketCfg); | |
148 | if (ret != 0) { | |
677f9d2b | 149 | throw std::runtime_error("Error creating a xsk socket of if_name " + ifName + ": " + stringerror(ret)); |
a61dd3f3 | 150 | } |
677f9d2b | 151 | socket = std::unique_ptr<xsk_socket, decltype(&xsk_socket__delete)>(tmp, xsk_socket__delete); |
a61dd3f3 | 152 | } |
677f9d2b RG |
153 | |
154 | uniqueEmptyFrameOffset.reserve(frameNum); | |
155 | { | |
a8c3569c RG |
156 | for (uint64_t idx = 0; idx < frameNum; idx++) { |
157 | uniqueEmptyFrameOffset.push_back(idx * frameSize + XDP_PACKET_HEADROOM); | |
677f9d2b RG |
158 | #ifdef DEBUG_UMEM |
159 | { | |
160 | auto umems = s_umems.lock(); | |
a8c3569c | 161 | (*umems)[idx * frameSize + XDP_PACKET_HEADROOM] = UmemEntryStatus(); |
677f9d2b RG |
162 | } |
163 | #endif /* DEBUG_UMEM */ | |
164 | } | |
a61dd3f3 | 165 | } |
677f9d2b | 166 | |
a61dd3f3 | 167 | fillFq(fqCapacity); |
677f9d2b | 168 | |
a61dd3f3 Y |
169 | const auto xskfd = xskFd(); |
170 | fds.push_back(pollfd{ | |
171 | .fd = xskfd, | |
172 | .events = POLLIN, | |
173 | .revents = 0}); | |
677f9d2b | 174 | |
a61dd3f3 | 175 | const auto xskMapFd = FDWrapper(bpf_obj_get(xskMapPath.c_str())); |
677f9d2b | 176 | |
a61dd3f3 | 177 | if (xskMapFd.getHandle() < 0) { |
bbb193ae | 178 | throw std::runtime_error("Error getting BPF map from path '" + xskMapPath + "'"); |
a61dd3f3 | 179 | } |
677f9d2b | 180 | |
a61dd3f3 | 181 | auto ret = bpf_map_update_elem(xskMapFd.getHandle(), &queue_id, &xskfd, 0); |
2a5e9760 | 182 | if (ret != 0) { |
bbb193ae | 183 | throw std::runtime_error("Error inserting into xsk_map '" + xskMapPath + "': " + std::to_string(ret)); |
a61dd3f3 Y |
184 | } |
185 | } | |
677f9d2b | 186 | |
f140f2b8 | 187 | // see xdp.h in contrib/ |
f140f2b8 RG |
188 | struct IPv4AndPort |
189 | { | |
190 | uint32_t addr; | |
191 | uint16_t port; | |
192 | }; | |
193 | struct IPv6AndPort | |
194 | { | |
195 | struct in6_addr addr; | |
196 | uint16_t port; | |
197 | }; | |
198 | ||
2a5e9760 | 199 | static FDWrapper getDestinationMap(const std::string& mapPath) |
f140f2b8 | 200 | { |
2a5e9760 | 201 | auto destMapFd = FDWrapper(bpf_obj_get(mapPath.c_str())); |
f140f2b8 RG |
202 | if (destMapFd.getHandle() < 0) { |
203 | throw std::runtime_error("Error getting the XSK destination addresses map path '" + mapPath + "'"); | |
204 | } | |
2a5e9760 RG |
205 | return destMapFd; |
206 | } | |
f140f2b8 | 207 | |
2a5e9760 RG |
208 | void XskSocket::clearDestinationMap(const std::string& mapPath, bool isV6) |
209 | { | |
210 | auto destMapFd = getDestinationMap(mapPath); | |
211 | if (!isV6) { | |
f140f2b8 RG |
212 | IPv4AndPort prevKey{}; |
213 | IPv4AndPort key{}; | |
214 | while (bpf_map_get_next_key(destMapFd.getHandle(), &prevKey, &key) == 0) { | |
215 | bpf_map_delete_elem(destMapFd.getHandle(), &key); | |
216 | prevKey = key; | |
217 | } | |
218 | } | |
219 | else { | |
220 | IPv6AndPort prevKey{}; | |
221 | IPv6AndPort key{}; | |
222 | while (bpf_map_get_next_key(destMapFd.getHandle(), &prevKey, &key) == 0) { | |
223 | bpf_map_delete_elem(destMapFd.getHandle(), &key); | |
224 | prevKey = key; | |
225 | } | |
226 | } | |
227 | } | |
228 | ||
2a5e9760 | 229 | void XskSocket::addDestinationAddress(const std::string& mapPath, const ComboAddress& destination) |
f140f2b8 | 230 | { |
2a5e9760 | 231 | auto destMapFd = getDestinationMap(mapPath); |
f140f2b8 RG |
232 | bool value = true; |
233 | if (destination.isIPv4()) { | |
234 | IPv4AndPort key{}; | |
235 | key.addr = destination.sin4.sin_addr.s_addr; | |
236 | key.port = destination.sin4.sin_port; | |
237 | auto ret = bpf_map_update_elem(destMapFd.getHandle(), &key, &value, 0); | |
2a5e9760 | 238 | if (ret != 0) { |
f140f2b8 RG |
239 | throw std::runtime_error("Error inserting into xsk_map '" + mapPath + "': " + std::to_string(ret)); |
240 | } | |
241 | } | |
242 | else { | |
243 | IPv6AndPort key{}; | |
244 | key.addr = destination.sin6.sin6_addr; | |
245 | key.port = destination.sin6.sin6_port; | |
246 | auto ret = bpf_map_update_elem(destMapFd.getHandle(), &key, &value, 0); | |
2a5e9760 | 247 | if (ret != 0) { |
f140f2b8 RG |
248 | throw std::runtime_error("Error inserting into XSK destination addresses map '" + mapPath + "': " + std::to_string(ret)); |
249 | } | |
250 | } | |
251 | } | |
252 | ||
2a5e9760 | 253 | void XskSocket::removeDestinationAddress(const std::string& mapPath, const ComboAddress& destination) |
f140f2b8 | 254 | { |
2a5e9760 | 255 | auto destMapFd = getDestinationMap(mapPath); |
f140f2b8 RG |
256 | if (destination.isIPv4()) { |
257 | IPv4AndPort key{}; | |
258 | key.addr = destination.sin4.sin_addr.s_addr; | |
259 | key.port = destination.sin4.sin_port; | |
260 | bpf_map_delete_elem(destMapFd.getHandle(), &key); | |
261 | } | |
262 | else { | |
263 | IPv6AndPort key{}; | |
264 | key.addr = destination.sin6.sin6_addr; | |
265 | key.port = destination.sin6.sin6_port; | |
266 | bpf_map_delete_elem(destMapFd.getHandle(), &key); | |
267 | } | |
268 | } | |
269 | ||
a61dd3f3 Y |
270 | void XskSocket::fillFq(uint32_t fillSize) noexcept |
271 | { | |
272 | { | |
cce416f2 RG |
273 | // if we have less than holdThreshold frames in the shared queue (which might be an issue |
274 | // when the XskWorker needs empty frames), move frames from the unique container into the | |
275 | // shared one. This might not be optimal right now. | |
a61dd3f3 Y |
276 | auto frames = sharedEmptyFrameOffset->lock(); |
277 | if (frames->size() < holdThreshold) { | |
278 | const auto moveSize = std::min(holdThreshold - frames->size(), uniqueEmptyFrameOffset.size()); | |
279 | if (moveSize > 0) { | |
cce416f2 | 280 | // NOLINTNEXTLINE(bugprone-narrowing-conversions,cppcoreguidelines-narrowing-conversions) |
a61dd3f3 | 281 | frames->insert(frames->end(), std::make_move_iterator(uniqueEmptyFrameOffset.end() - moveSize), std::make_move_iterator(uniqueEmptyFrameOffset.end())); |
677f9d2b | 282 | uniqueEmptyFrameOffset.resize(uniqueEmptyFrameOffset.size() - moveSize); |
a61dd3f3 Y |
283 | } |
284 | } | |
285 | } | |
677f9d2b | 286 | |
a61dd3f3 Y |
287 | if (uniqueEmptyFrameOffset.size() < fillSize) { |
288 | return; | |
289 | } | |
677f9d2b RG |
290 | |
291 | uint32_t idx{0}; | |
292 | auto toFill = xsk_ring_prod__reserve(&fq, fillSize, &idx); | |
293 | if (toFill == 0) { | |
a61dd3f3 Y |
294 | return; |
295 | } | |
954a9898 | 296 | uint32_t processed = 0; |
677f9d2b | 297 | for (; processed < toFill; processed++) { |
a61dd3f3 | 298 | *xsk_ring_prod__fill_addr(&fq, idx++) = uniqueEmptyFrameOffset.back(); |
677f9d2b RG |
299 | #ifdef DEBUG_UMEM |
300 | checkUmemIntegrity(__PRETTY_FUNCTION__, __LINE__, uniqueEmptyFrameOffset.back(), {UmemEntryStatus::Status::Free}, UmemEntryStatus::Status::FillQueue); | |
301 | #endif /* DEBUG_UMEM */ | |
a61dd3f3 Y |
302 | uniqueEmptyFrameOffset.pop_back(); |
303 | } | |
677f9d2b | 304 | |
954a9898 | 305 | xsk_ring_prod__submit(&fq, processed); |
a61dd3f3 | 306 | } |
677f9d2b | 307 | |
a61dd3f3 Y |
308 | int XskSocket::wait(int timeout) |
309 | { | |
677f9d2b RG |
310 | auto waitAtMost = std::min(timeout, firstTimeout()); |
311 | return poll(fds.data(), fds.size(), waitAtMost); | |
a61dd3f3 | 312 | } |
677f9d2b | 313 | |
a61dd3f3 Y |
314 | [[nodiscard]] uint64_t XskSocket::frameOffset(const XskPacket& packet) const noexcept |
315 | { | |
ea247879 | 316 | return packet.getFrameOffsetFrom(umem.bufBase); |
a61dd3f3 Y |
317 | } |
318 | ||
cce416f2 RG |
319 | [[nodiscard]] int XskSocket::xskFd() const noexcept |
320 | { | |
677f9d2b RG |
321 | return xsk_socket__fd(socket.get()); |
322 | } | |
a61dd3f3 | 323 | |
ae61f00a | 324 | void XskSocket::send(std::vector<XskPacket>& packets) |
a61dd3f3 | 325 | { |
2a5e9760 | 326 | while (!packets.empty()) { |
677f9d2b RG |
327 | auto packetSize = packets.size(); |
328 | if (packetSize > std::numeric_limits<uint32_t>::max()) { | |
329 | packetSize = std::numeric_limits<uint32_t>::max(); | |
330 | } | |
331 | size_t toSend = std::min(static_cast<uint32_t>(packetSize), txCapacity); | |
332 | uint32_t idx{0}; | |
333 | auto toFill = xsk_ring_prod__reserve(&tx, toSend, &idx); | |
334 | if (toFill == 0) { | |
335 | return; | |
336 | } | |
a61dd3f3 | 337 | |
677f9d2b RG |
338 | size_t queued = 0; |
339 | for (const auto& packet : packets) { | |
340 | if (queued == toFill) { | |
341 | break; | |
342 | } | |
343 | *xsk_ring_prod__tx_desc(&tx, idx++) = { | |
ae61f00a RG |
344 | .addr = frameOffset(packet), |
345 | .len = packet.getFrameLen(), | |
677f9d2b RG |
346 | .options = 0}; |
347 | #ifdef DEBUG_UMEM | |
ae61f00a | 348 | checkUmemIntegrity(__PRETTY_FUNCTION__, __LINE__, frameOffset(packet), {UmemEntryStatus::Status::Free, UmemEntryStatus::Status::Received}, UmemEntryStatus::Status::TXQueue); |
677f9d2b RG |
349 | #endif /* DEBUG_UMEM */ |
350 | queued++; | |
351 | } | |
352 | xsk_ring_prod__submit(&tx, toFill); | |
353 | packets.erase(packets.begin(), packets.begin() + toFill); | |
a61dd3f3 | 354 | } |
a61dd3f3 | 355 | } |
677f9d2b | 356 | |
ae61f00a | 357 | std::vector<XskPacket> XskSocket::recv(uint32_t recvSizeMax, uint32_t* failedCount) |
a61dd3f3 | 358 | { |
677f9d2b | 359 | uint32_t idx{0}; |
ae61f00a | 360 | std::vector<XskPacket> res; |
b1dd62ee | 361 | // how many descriptors to packets have been filled |
a61dd3f3 | 362 | const auto recvSize = xsk_ring_cons__peek(&rx, recvSizeMax, &idx); |
677f9d2b | 363 | if (recvSize == 0) { |
a61dd3f3 Y |
364 | return res; |
365 | } | |
b1dd62ee | 366 | |
2a5e9760 | 367 | // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast) |
a61dd3f3 | 368 | const auto baseAddr = reinterpret_cast<uint64_t>(umem.bufBase); |
954a9898 RG |
369 | uint32_t failed = 0; |
370 | uint32_t processed = 0; | |
677f9d2b | 371 | res.reserve(recvSize); |
954a9898 | 372 | for (; processed < recvSize; processed++) { |
677f9d2b RG |
373 | try { |
374 | const auto* desc = xsk_ring_cons__rx_desc(&rx, idx++); | |
cce416f2 | 375 | // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast,performance-no-int-to-ptr) |
ae61f00a | 376 | XskPacket packet = XskPacket(reinterpret_cast<uint8_t*>(desc->addr + baseAddr), desc->len, frameSize); |
677f9d2b | 377 | #ifdef DEBUG_UMEM |
ae61f00a | 378 | checkUmemIntegrity(__PRETTY_FUNCTION__, __LINE__, frameOffset(packet), {UmemEntryStatus::Status::Free, UmemEntryStatus::Status::FillQueue}, UmemEntryStatus::Status::Received); |
677f9d2b RG |
379 | #endif /* DEBUG_UMEM */ |
380 | ||
ae61f00a | 381 | if (!packet.parse(false)) { |
677f9d2b | 382 | ++failed; |
2a5e9760 | 383 | markAsFree(packet); |
677f9d2b RG |
384 | } |
385 | else { | |
2a5e9760 | 386 | res.push_back(packet); |
677f9d2b RG |
387 | } |
388 | } | |
389 | catch (const std::exception& exp) { | |
390 | std::cerr << "Exception while processing the XSK RX queue: " << exp.what() << std::endl; | |
391 | break; | |
a61dd3f3 | 392 | } |
677f9d2b RG |
393 | catch (...) { |
394 | std::cerr << "Exception while processing the XSK RX queue" << std::endl; | |
395 | break; | |
a61dd3f3 Y |
396 | } |
397 | } | |
b1dd62ee RG |
398 | |
399 | // this releases the descriptor, but not the packet (umem entries) | |
400 | // which will only be made available again when pushed into the fill | |
401 | // queue | |
954a9898 | 402 | xsk_ring_cons__release(&rx, processed); |
2a5e9760 | 403 | if (failedCount != nullptr) { |
954a9898 | 404 | *failedCount = failed; |
a61dd3f3 | 405 | } |
b1dd62ee | 406 | |
a61dd3f3 Y |
407 | return res; |
408 | } | |
677f9d2b | 409 | |
ae61f00a | 410 | void XskSocket::pickUpReadyPacket(std::vector<XskPacket>& packets) |
a61dd3f3 | 411 | { |
2a5e9760 | 412 | timespec now{}; |
a61dd3f3 | 413 | gettime(&now); |
ae61f00a | 414 | while (!waitForDelay.empty() && timeDifference(now, waitForDelay.top().getSendTime()) <= 0) { |
2a5e9760 | 415 | // NOLINTNEXTLINE(cppcoreguidelines-pro-type-const-cast) |
ae61f00a | 416 | auto& top = const_cast<XskPacket&>(waitForDelay.top()); |
2a5e9760 | 417 | packets.push_back(top); |
a61dd3f3 Y |
418 | waitForDelay.pop(); |
419 | } | |
420 | } | |
677f9d2b | 421 | |
a61dd3f3 Y |
422 | void XskSocket::recycle(size_t size) noexcept |
423 | { | |
677f9d2b | 424 | uint32_t idx{0}; |
a61dd3f3 | 425 | const auto completeSize = xsk_ring_cons__peek(&cq, size, &idx); |
677f9d2b | 426 | if (completeSize == 0) { |
a61dd3f3 Y |
427 | return; |
428 | } | |
677f9d2b RG |
429 | uniqueEmptyFrameOffset.reserve(uniqueEmptyFrameOffset.size() + completeSize); |
430 | uint32_t processed = 0; | |
431 | for (; processed < completeSize; ++processed) { | |
a61dd3f3 | 432 | uniqueEmptyFrameOffset.push_back(*xsk_ring_cons__comp_addr(&cq, idx++)); |
677f9d2b RG |
433 | #ifdef DEBUG_UMEM |
434 | checkUmemIntegrity(__PRETTY_FUNCTION__, __LINE__, uniqueEmptyFrameOffset.back(), {UmemEntryStatus::Status::Received, UmemEntryStatus::Status::TXQueue}, UmemEntryStatus::Status::Free); | |
435 | #endif /* DEBUG_UMEM */ | |
a61dd3f3 | 436 | } |
677f9d2b | 437 | xsk_ring_cons__release(&cq, processed); |
a61dd3f3 Y |
438 | } |
439 | ||
4dd6b4b4 | 440 | void XskSocket::XskUmem::umemInit(size_t memSize, xsk_ring_cons* completionQueue, xsk_ring_prod* fillQueue, xsk_umem_config* config) |
a61dd3f3 Y |
441 | { |
442 | size = memSize; | |
443 | bufBase = static_cast<uint8_t*>(mmap(nullptr, size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0)); | |
444 | if (bufBase == MAP_FAILED) { | |
445 | throw std::runtime_error("mmap failed"); | |
446 | } | |
4dd6b4b4 | 447 | auto ret = xsk_umem__create(&umem, bufBase, size, fillQueue, completionQueue, config); |
a61dd3f3 Y |
448 | if (ret != 0) { |
449 | munmap(bufBase, size); | |
677f9d2b | 450 | throw std::runtime_error("Error creating a umem of size " + std::to_string(size) + ": " + stringerror(ret)); |
a61dd3f3 Y |
451 | } |
452 | } | |
453 | ||
57131ee9 RG |
454 | std::string XskSocket::getMetrics() const |
455 | { | |
2a5e9760 | 456 | xdp_statistics stats{}; |
57131ee9 RG |
457 | socklen_t optlen = sizeof(stats); |
458 | int err = getsockopt(xskFd(), SOL_XDP, XDP_STATISTICS, &stats, &optlen); | |
2a5e9760 | 459 | if (err != 0) { |
57131ee9 RG |
460 | return ""; |
461 | } | |
462 | if (optlen != sizeof(struct xdp_statistics)) { | |
463 | return ""; | |
464 | } | |
465 | ||
466 | ostringstream ret; | |
467 | ret << "RX dropped: " << std::to_string(stats.rx_dropped) << std::endl; | |
468 | ret << "RX invalid descs: " << std::to_string(stats.rx_invalid_descs) << std::endl; | |
469 | ret << "TX invalid descs: " << std::to_string(stats.tx_invalid_descs) << std::endl; | |
470 | ret << "RX ring full: " << std::to_string(stats.rx_ring_full) << std::endl; | |
471 | ret << "RX fill ring empty descs: " << std::to_string(stats.rx_fill_ring_empty_descs) << std::endl; | |
677f9d2b | 472 | ret << "TX ring empty descs: " << std::to_string(stats.tx_ring_empty_descs) << std::endl; |
57131ee9 RG |
473 | return ret.str(); |
474 | } | |
475 | ||
73dc64ab RG |
476 | [[nodiscard]] std::string XskSocket::getXDPMode() const |
477 | { | |
265ece9c | 478 | #ifdef HAVE_BPF_XDP_QUERY |
73dc64ab RG |
479 | unsigned int itfIdx = if_nametoindex(ifName.c_str()); |
480 | if (itfIdx == 0) { | |
265ece9c | 481 | return "unable to get interface index"; |
73dc64ab | 482 | } |
40f605d2 RG |
483 | bpf_xdp_query_opts info{}; |
484 | info.sz = sizeof(info); | |
a8c3569c | 485 | int ret = bpf_xdp_query(static_cast<int>(itfIdx), 0, &info); |
73dc64ab RG |
486 | if (ret != 0) { |
487 | return {}; | |
488 | } | |
489 | switch (info.attach_mode) { | |
490 | case XDP_ATTACHED_DRV: | |
491 | case XDP_ATTACHED_HW: | |
492 | return "native"; | |
493 | case XDP_ATTACHED_SKB: | |
494 | return "emulated"; | |
495 | default: | |
496 | return "unknown"; | |
497 | } | |
265ece9c RG |
498 | #else /* HAVE_BPF_XDP_QUERY */ |
499 | return "undetected"; | |
500 | #endif /* HAVE_BPF_XDP_QUERY */ | |
73dc64ab RG |
501 | } |
502 | ||
2a5e9760 | 503 | void XskSocket::markAsFree(const XskPacket& packet) |
677f9d2b | 504 | { |
ae61f00a | 505 | auto offset = frameOffset(packet); |
677f9d2b RG |
506 | #ifdef DEBUG_UMEM |
507 | checkUmemIntegrity(__PRETTY_FUNCTION__, __LINE__, offset, {UmemEntryStatus::Status::Received, UmemEntryStatus::Status::TXQueue}, UmemEntryStatus::Status::Free); | |
508 | #endif /* DEBUG_UMEM */ | |
509 | ||
510 | uniqueEmptyFrameOffset.push_back(offset); | |
677f9d2b RG |
511 | } |
512 | ||
a61dd3f3 Y |
513 | XskSocket::XskUmem::~XskUmem() |
514 | { | |
2a5e9760 | 515 | if (umem != nullptr) { |
a61dd3f3 Y |
516 | xsk_umem__delete(umem); |
517 | } | |
2a5e9760 | 518 | if (bufBase != nullptr) { |
a61dd3f3 Y |
519 | munmap(bufBase, size); |
520 | } | |
521 | } | |
522 | ||
677f9d2b RG |
523 | [[nodiscard]] size_t XskPacket::getL4HeaderOffset() const noexcept |
524 | { | |
525 | return sizeof(ethhdr) + (v6 ? (sizeof(ipv6hdr)) : sizeof(iphdr)); | |
526 | } | |
527 | ||
528 | [[nodiscard]] size_t XskPacket::getDataOffset() const noexcept | |
529 | { | |
530 | return getL4HeaderOffset() + sizeof(udphdr); | |
531 | } | |
532 | ||
533 | [[nodiscard]] size_t XskPacket::getDataSize() const noexcept | |
534 | { | |
535 | return frameLength - getDataOffset(); | |
536 | } | |
537 | ||
538 | [[nodiscard]] ethhdr XskPacket::getEthernetHeader() const noexcept | |
539 | { | |
540 | ethhdr ethHeader{}; | |
f140f2b8 RG |
541 | if (frameLength >= sizeof(ethHeader)) { |
542 | memcpy(ðHeader, frame, sizeof(ethHeader)); | |
543 | } | |
677f9d2b RG |
544 | return ethHeader; |
545 | } | |
546 | ||
547 | void XskPacket::setEthernetHeader(const ethhdr& ethHeader) noexcept | |
a61dd3f3 | 548 | { |
f140f2b8 RG |
549 | if (frameLength < sizeof(ethHeader)) { |
550 | frameLength = sizeof(ethHeader); | |
551 | } | |
677f9d2b RG |
552 | memcpy(frame, ðHeader, sizeof(ethHeader)); |
553 | } | |
554 | ||
555 | [[nodiscard]] iphdr XskPacket::getIPv4Header() const noexcept | |
556 | { | |
557 | iphdr ipv4Header{}; | |
558 | assert(frameLength >= (sizeof(ethhdr) + sizeof(ipv4Header))); | |
559 | assert(!v6); | |
2a5e9760 | 560 | // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) |
677f9d2b RG |
561 | memcpy(&ipv4Header, frame + sizeof(ethhdr), sizeof(ipv4Header)); |
562 | return ipv4Header; | |
563 | } | |
564 | ||
565 | void XskPacket::setIPv4Header(const iphdr& ipv4Header) noexcept | |
566 | { | |
567 | assert(frameLength >= (sizeof(ethhdr) + sizeof(iphdr))); | |
568 | assert(!v6); | |
2a5e9760 | 569 | // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) |
677f9d2b RG |
570 | memcpy(frame + sizeof(ethhdr), &ipv4Header, sizeof(ipv4Header)); |
571 | } | |
572 | ||
573 | [[nodiscard]] ipv6hdr XskPacket::getIPv6Header() const noexcept | |
574 | { | |
575 | ipv6hdr ipv6Header{}; | |
576 | assert(frameLength >= (sizeof(ethhdr) + sizeof(ipv6Header))); | |
577 | assert(v6); | |
2a5e9760 | 578 | // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) |
677f9d2b RG |
579 | memcpy(&ipv6Header, frame + sizeof(ethhdr), sizeof(ipv6Header)); |
580 | return ipv6Header; | |
581 | } | |
582 | ||
583 | void XskPacket::setIPv6Header(const ipv6hdr& ipv6Header) noexcept | |
584 | { | |
585 | assert(frameLength >= (sizeof(ethhdr) + sizeof(ipv6Header))); | |
586 | assert(v6); | |
2a5e9760 | 587 | // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) |
677f9d2b RG |
588 | memcpy(frame + sizeof(ethhdr), &ipv6Header, sizeof(ipv6Header)); |
589 | } | |
590 | ||
591 | [[nodiscard]] udphdr XskPacket::getUDPHeader() const noexcept | |
592 | { | |
593 | udphdr udpHeader{}; | |
594 | assert(frameLength >= (sizeof(ethhdr) + (v6 ? sizeof(ipv6hdr) : sizeof(iphdr)) + sizeof(udpHeader))); | |
2a5e9760 | 595 | // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) |
677f9d2b RG |
596 | memcpy(&udpHeader, frame + getL4HeaderOffset(), sizeof(udpHeader)); |
597 | return udpHeader; | |
598 | } | |
599 | ||
600 | void XskPacket::setUDPHeader(const udphdr& udpHeader) noexcept | |
601 | { | |
602 | assert(frameLength >= (sizeof(ethhdr) + (v6 ? sizeof(ipv6hdr) : sizeof(iphdr)) + sizeof(udpHeader))); | |
cce416f2 | 603 | // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) |
677f9d2b RG |
604 | memcpy(frame + getL4HeaderOffset(), &udpHeader, sizeof(udpHeader)); |
605 | } | |
606 | ||
607 | bool XskPacket::parse(bool fromSetHeader) | |
608 | { | |
609 | if (frameLength <= sizeof(ethhdr)) { | |
610 | return false; | |
611 | } | |
612 | ||
613 | auto ethHeader = getEthernetHeader(); | |
614 | uint8_t l4Protocol{0}; | |
615 | if (ethHeader.h_proto == htons(ETH_P_IP)) { | |
616 | if (frameLength < (sizeof(ethhdr) + sizeof(iphdr) + sizeof(udphdr))) { | |
617 | return false; | |
618 | } | |
619 | v6 = false; | |
620 | auto ipHeader = getIPv4Header(); | |
621 | if (ipHeader.ihl != (static_cast<uint8_t>(sizeof(iphdr) / 4))) { | |
a61dd3f3 Y |
622 | // ip options is not supported now! |
623 | return false; | |
624 | } | |
625 | // check ip.check == ipv4Checksum() is not needed! | |
626 | // We check it in BPF program | |
677f9d2b | 627 | // we don't, actually. |
2a5e9760 | 628 | // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast) |
677f9d2b | 629 | from = makeComboAddressFromRaw(4, reinterpret_cast<const char*>(&ipHeader.saddr), sizeof(ipHeader.saddr)); |
2a5e9760 | 630 | // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast) |
677f9d2b RG |
631 | to = makeComboAddressFromRaw(4, reinterpret_cast<const char*>(&ipHeader.daddr), sizeof(ipHeader.daddr)); |
632 | l4Protocol = ipHeader.protocol; | |
633 | if (!fromSetHeader && (frameLength - sizeof(ethhdr)) != ntohs(ipHeader.tot_len)) { | |
634 | // too small, or too large (trailing data), go away | |
635 | return false; | |
636 | } | |
637 | } | |
638 | else if (ethHeader.h_proto == htons(ETH_P_IPV6)) { | |
639 | if (frameLength < (sizeof(ethhdr) + sizeof(ipv6hdr) + sizeof(udphdr))) { | |
640 | return false; | |
641 | } | |
642 | v6 = true; | |
643 | auto ipHeader = getIPv6Header(); | |
2a5e9760 | 644 | // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast) |
677f9d2b | 645 | from = makeComboAddressFromRaw(6, reinterpret_cast<const char*>(&ipHeader.saddr), sizeof(ipHeader.saddr)); |
2a5e9760 | 646 | // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast) |
677f9d2b RG |
647 | to = makeComboAddressFromRaw(6, reinterpret_cast<const char*>(&ipHeader.daddr), sizeof(ipHeader.daddr)); |
648 | l4Protocol = ipHeader.nexthdr; | |
649 | if (!fromSetHeader && (frameLength - (sizeof(ethhdr) + sizeof(ipv6hdr))) != ntohs(ipHeader.payload_len)) { | |
a61dd3f3 Y |
650 | return false; |
651 | } | |
a61dd3f3 Y |
652 | } |
653 | else { | |
654 | return false; | |
655 | } | |
677f9d2b RG |
656 | |
657 | if (l4Protocol != IPPROTO_UDP) { | |
658 | return false; | |
a61dd3f3 | 659 | } |
677f9d2b RG |
660 | |
661 | // check udp.check == ipv4Checksum() is not needed! | |
662 | // We check it in BPF program | |
663 | // we don't, actually. | |
664 | auto udpHeader = getUDPHeader(); | |
665 | if (!fromSetHeader) { | |
666 | // Because of XskPacket::setHeader | |
667 | if (getDataOffset() > frameLength) { | |
a61dd3f3 Y |
668 | return false; |
669 | } | |
677f9d2b RG |
670 | |
671 | if (getDataSize() + sizeof(udphdr) != ntohs(udpHeader.len)) { | |
a61dd3f3 Y |
672 | return false; |
673 | } | |
a61dd3f3 | 674 | } |
677f9d2b RG |
675 | |
676 | from.setPort(ntohs(udpHeader.source)); | |
677 | to.setPort(ntohs(udpHeader.dest)); | |
678 | return true; | |
a61dd3f3 Y |
679 | } |
680 | ||
677f9d2b | 681 | uint32_t XskPacket::getDataLen() const noexcept |
a61dd3f3 | 682 | { |
677f9d2b | 683 | return getDataSize(); |
a61dd3f3 | 684 | } |
677f9d2b RG |
685 | |
686 | uint32_t XskPacket::getFrameLen() const noexcept | |
a61dd3f3 | 687 | { |
677f9d2b | 688 | return frameLength; |
a61dd3f3 | 689 | } |
677f9d2b RG |
690 | |
691 | size_t XskPacket::getCapacity() const noexcept | |
a61dd3f3 | 692 | { |
677f9d2b | 693 | return frameSize; |
a61dd3f3 Y |
694 | } |
695 | ||
696 | void XskPacket::changeDirectAndUpdateChecksum() noexcept | |
697 | { | |
677f9d2b | 698 | auto ethHeader = getEthernetHeader(); |
a61dd3f3 | 699 | { |
cce416f2 | 700 | std::array<uint8_t, ETH_ALEN> tmp{}; |
2a5e9760 RG |
701 | static_assert(tmp.size() == sizeof(ethHeader.h_dest), "Size Error"); |
702 | static_assert(tmp.size() == sizeof(ethHeader.h_source), "Size Error"); | |
cce416f2 | 703 | // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-array-to-pointer-decay) |
2a5e9760 | 704 | memcpy(tmp.data(), ethHeader.h_dest, tmp.size()); |
cce416f2 | 705 | // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-array-to-pointer-decay) |
2a5e9760 | 706 | memcpy(ethHeader.h_dest, ethHeader.h_source, tmp.size()); |
cce416f2 | 707 | // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-array-to-pointer-decay) |
2a5e9760 | 708 | memcpy(ethHeader.h_source, tmp.data(), tmp.size()); |
a61dd3f3 | 709 | } |
677f9d2b | 710 | if (ethHeader.h_proto == htons(ETH_P_IPV6)) { |
a61dd3f3 | 711 | // IPV6 |
677f9d2b RG |
712 | auto ipv6 = getIPv6Header(); |
713 | std::swap(ipv6.daddr, ipv6.saddr); | |
714 | assert(ipv6.nexthdr == IPPROTO_UDP); | |
715 | ||
716 | auto udp = getUDPHeader(); | |
717 | std::swap(udp.dest, udp.source); | |
718 | udp.len = htons(getDataSize() + sizeof(udp)); | |
719 | udp.check = 0; | |
720 | /* needed to get the correct checksum */ | |
721 | setIPv6Header(ipv6); | |
722 | setUDPHeader(udp); | |
723 | udp.check = tcp_udp_v6_checksum(&ipv6); | |
724 | rewriteIpv6Header(&ipv6, getFrameLen()); | |
725 | setIPv6Header(ipv6); | |
726 | setUDPHeader(udp); | |
a61dd3f3 Y |
727 | } |
728 | else { | |
729 | // IPV4 | |
677f9d2b RG |
730 | auto ipv4 = getIPv4Header(); |
731 | std::swap(ipv4.daddr, ipv4.saddr); | |
732 | assert(ipv4.protocol == IPPROTO_UDP); | |
733 | ||
734 | auto udp = getUDPHeader(); | |
735 | std::swap(udp.dest, udp.source); | |
736 | udp.len = htons(getDataSize() + sizeof(udp)); | |
737 | udp.check = 0; | |
738 | /* needed to get the correct checksum */ | |
739 | setIPv4Header(ipv4); | |
740 | setUDPHeader(udp); | |
741 | udp.check = tcp_udp_v4_checksum(&ipv4); | |
742 | rewriteIpv4Header(&ipv4, getFrameLen()); | |
743 | setIPv4Header(ipv4); | |
744 | setUDPHeader(udp); | |
a61dd3f3 | 745 | } |
677f9d2b | 746 | setEthernetHeader(ethHeader); |
a61dd3f3 | 747 | } |
677f9d2b RG |
748 | |
749 | void XskPacket::rewriteIpv4Header(struct iphdr* ipv4header, size_t frameLen) noexcept | |
a61dd3f3 | 750 | { |
677f9d2b RG |
751 | ipv4header->version = 4; |
752 | ipv4header->ihl = sizeof(iphdr) / 4; | |
753 | ipv4header->tos = 0; | |
754 | ipv4header->tot_len = htons(frameLen - sizeof(ethhdr)); | |
755 | ipv4header->id = 0; | |
756 | ipv4header->frag_off = 0; | |
757 | ipv4header->ttl = DefaultTTL; | |
758 | ipv4header->check = 0; | |
759 | ipv4header->check = ipv4Checksum(ipv4header); | |
a61dd3f3 | 760 | } |
677f9d2b RG |
761 | |
762 | void XskPacket::rewriteIpv6Header(struct ipv6hdr* ipv6header, size_t frameLen) noexcept | |
a61dd3f3 | 763 | { |
677f9d2b RG |
764 | ipv6header->version = 6; |
765 | ipv6header->priority = 0; | |
766 | ipv6header->payload_len = htons(frameLen - sizeof(ethhdr) - sizeof(ipv6hdr)); | |
767 | ipv6header->hop_limit = DefaultTTL; | |
768 | memset(&ipv6header->flow_lbl, 0, sizeof(ipv6header->flow_lbl)); | |
a61dd3f3 Y |
769 | } |
770 | ||
771 | bool XskPacket::isIPV6() const noexcept | |
772 | { | |
677f9d2b | 773 | return v6; |
a61dd3f3 | 774 | } |
677f9d2b | 775 | |
f140f2b8 RG |
776 | XskPacket::XskPacket(uint8_t* frame_, size_t dataSize, size_t frameSize_) : |
777 | frame(frame_), frameLength(dataSize), frameSize(frameSize_ - XDP_PACKET_HEADROOM) | |
a61dd3f3 Y |
778 | { |
779 | } | |
677f9d2b | 780 | |
a61dd3f3 Y |
781 | PacketBuffer XskPacket::clonePacketBuffer() const |
782 | { | |
677f9d2b | 783 | const auto size = getDataSize(); |
a61dd3f3 | 784 | PacketBuffer tmp(size); |
a8c3569c RG |
785 | if (size > 0) { |
786 | // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) | |
787 | memcpy(tmp.data(), frame + getDataOffset(), size); | |
788 | } | |
a61dd3f3 Y |
789 | return tmp; |
790 | } | |
677f9d2b | 791 | |
a61dd3f3 Y |
792 | bool XskPacket::setPayload(const PacketBuffer& buf) |
793 | { | |
794 | const auto bufSize = buf.size(); | |
677f9d2b RG |
795 | const auto currentCapacity = getCapacity(); |
796 | if (bufSize == 0 || bufSize > currentCapacity) { | |
a61dd3f3 Y |
797 | return false; |
798 | } | |
799 | flags |= UPDATE; | |
2a5e9760 | 800 | // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) |
677f9d2b | 801 | memcpy(frame + getDataOffset(), buf.data(), bufSize); |
2a5e9760 | 802 | // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) |
677f9d2b | 803 | frameLength = getDataOffset() + bufSize; |
a61dd3f3 Y |
804 | return true; |
805 | } | |
677f9d2b | 806 | |
a61dd3f3 Y |
807 | void XskPacket::addDelay(const int relativeMilliseconds) noexcept |
808 | { | |
809 | gettime(&sendTime); | |
2a5e9760 | 810 | sendTime.tv_nsec += static_cast<int64_t>(relativeMilliseconds) * 1000000L; |
a61dd3f3 Y |
811 | sendTime.tv_sec += sendTime.tv_nsec / 1000000000L; |
812 | sendTime.tv_nsec %= 1000000000L; | |
813 | } | |
677f9d2b | 814 | |
2a5e9760 | 815 | bool operator<(const XskPacket& lhs, const XskPacket& rhs) noexcept |
a61dd3f3 | 816 | { |
2a5e9760 | 817 | return lhs.getSendTime() < rhs.getSendTime(); |
a61dd3f3 | 818 | } |
677f9d2b | 819 | |
a61dd3f3 Y |
820 | const ComboAddress& XskPacket::getFromAddr() const noexcept |
821 | { | |
822 | return from; | |
823 | } | |
677f9d2b | 824 | |
a61dd3f3 Y |
825 | const ComboAddress& XskPacket::getToAddr() const noexcept |
826 | { | |
827 | return to; | |
828 | } | |
677f9d2b | 829 | |
2a5e9760 | 830 | void XskWorker::notify(int desc) |
a61dd3f3 Y |
831 | { |
832 | uint64_t value = 1; | |
833 | ssize_t res = 0; | |
2a5e9760 | 834 | while ((res = write(desc, &value, sizeof(value))) == EINTR) { |
a61dd3f3 Y |
835 | } |
836 | if (res != sizeof(value)) { | |
837 | throw runtime_error("Unable Wake Up XskSocket Failed"); | |
838 | } | |
839 | } | |
677f9d2b | 840 | |
a61dd3f3 Y |
841 | XskWorker::XskWorker() : |
842 | workerWaker(createEventfd()), xskSocketWaker(createEventfd()) | |
843 | { | |
844 | } | |
4dd6b4b4 | 845 | |
2a5e9760 | 846 | void XskWorker::pushToProcessingQueue(XskPacket& packet) |
4dd6b4b4 | 847 | { |
677f9d2b | 848 | #if defined(__SANITIZE_THREAD__) |
2a5e9760 | 849 | if (!incomingPacketsQueue.lock()->push(packet)) { |
677f9d2b | 850 | #else |
2a5e9760 | 851 | if (!incomingPacketsQueue.push(packet)) { |
677f9d2b | 852 | #endif |
2a5e9760 | 853 | markAsFree(packet); |
4dd6b4b4 RG |
854 | } |
855 | } | |
856 | ||
2a5e9760 | 857 | void XskWorker::pushToSendQueue(XskPacket& packet) |
4dd6b4b4 | 858 | { |
677f9d2b | 859 | #if defined(__SANITIZE_THREAD__) |
2a5e9760 | 860 | if (!outgoingPacketsQueue.lock()->push(packet)) { |
677f9d2b | 861 | #else |
2a5e9760 | 862 | if (!outgoingPacketsQueue.push(packet)) { |
677f9d2b | 863 | #endif |
2a5e9760 | 864 | markAsFree(packet); |
4dd6b4b4 RG |
865 | } |
866 | } | |
867 | ||
677f9d2b | 868 | const void* XskPacket::getPayloadData() const |
a61dd3f3 | 869 | { |
2a5e9760 | 870 | // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) |
677f9d2b | 871 | return frame + getDataOffset(); |
a61dd3f3 | 872 | } |
677f9d2b RG |
873 | |
874 | void XskPacket::setAddr(const ComboAddress& from_, MACAddr fromMAC, const ComboAddress& to_, MACAddr toMAC) noexcept | |
a61dd3f3 | 875 | { |
677f9d2b | 876 | auto ethHeader = getEthernetHeader(); |
cce416f2 | 877 | // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-array-to-pointer-decay) |
2a5e9760 | 878 | memcpy(ethHeader.h_dest, toMAC.data(), toMAC.size()); |
cce416f2 | 879 | // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-array-to-pointer-decay) |
2a5e9760 | 880 | memcpy(ethHeader.h_source, fromMAC.data(), fromMAC.size()); |
677f9d2b | 881 | setEthernetHeader(ethHeader); |
a61dd3f3 Y |
882 | to = to_; |
883 | from = from_; | |
677f9d2b RG |
884 | v6 = !to.isIPv4(); |
885 | flags = 0; | |
a61dd3f3 | 886 | } |
677f9d2b | 887 | |
a61dd3f3 Y |
888 | void XskPacket::rewrite() noexcept |
889 | { | |
890 | flags |= REWRITE; | |
677f9d2b RG |
891 | auto ethHeader = getEthernetHeader(); |
892 | if (!v6) { | |
893 | ethHeader.h_proto = htons(ETH_P_IP); | |
894 | ||
895 | auto ipHeader = getIPv4Header(); | |
896 | ipHeader.daddr = to.sin4.sin_addr.s_addr; | |
897 | ipHeader.saddr = from.sin4.sin_addr.s_addr; | |
898 | ||
899 | auto udpHeader = getUDPHeader(); | |
900 | ipHeader.protocol = IPPROTO_UDP; | |
901 | udpHeader.source = from.sin4.sin_port; | |
902 | udpHeader.dest = to.sin4.sin_port; | |
f140f2b8 | 903 | udpHeader.len = htons(getDataSize() + sizeof(udpHeader)); |
677f9d2b RG |
904 | udpHeader.check = 0; |
905 | /* needed to get the correct checksum */ | |
906 | setIPv4Header(ipHeader); | |
907 | setUDPHeader(udpHeader); | |
908 | udpHeader.check = tcp_udp_v4_checksum(&ipHeader); | |
909 | rewriteIpv4Header(&ipHeader, getFrameLen()); | |
910 | setIPv4Header(ipHeader); | |
911 | setUDPHeader(udpHeader); | |
a61dd3f3 Y |
912 | } |
913 | else { | |
677f9d2b RG |
914 | ethHeader.h_proto = htons(ETH_P_IPV6); |
915 | ||
916 | auto ipHeader = getIPv6Header(); | |
917 | memcpy(&ipHeader.daddr, &to.sin6.sin6_addr, sizeof(ipHeader.daddr)); | |
918 | memcpy(&ipHeader.saddr, &from.sin6.sin6_addr, sizeof(ipHeader.saddr)); | |
919 | ||
920 | auto udpHeader = getUDPHeader(); | |
921 | ipHeader.nexthdr = IPPROTO_UDP; | |
922 | udpHeader.source = from.sin6.sin6_port; | |
923 | udpHeader.dest = to.sin6.sin6_port; | |
f928337f | 924 | udpHeader.len = htons(getDataSize() + sizeof(udpHeader)); |
677f9d2b RG |
925 | udpHeader.check = 0; |
926 | /* needed to get the correct checksum */ | |
927 | setIPv6Header(ipHeader); | |
928 | setUDPHeader(udpHeader); | |
929 | udpHeader.check = tcp_udp_v6_checksum(&ipHeader); | |
930 | setIPv6Header(ipHeader); | |
931 | setUDPHeader(udpHeader); | |
a61dd3f3 | 932 | } |
677f9d2b RG |
933 | |
934 | setEthernetHeader(ethHeader); | |
a61dd3f3 Y |
935 | } |
936 | ||
2a5e9760 | 937 | [[nodiscard]] __be16 XskPacket::ipv4Checksum(const struct iphdr* ipHeader) noexcept |
a61dd3f3 | 938 | { |
2a5e9760 | 939 | auto partial = ip_checksum_partial(ipHeader, sizeof(iphdr), 0); |
677f9d2b | 940 | return ip_checksum_fold(partial); |
a61dd3f3 | 941 | } |
677f9d2b | 942 | |
2a5e9760 | 943 | [[nodiscard]] __be16 XskPacket::tcp_udp_v4_checksum(const struct iphdr* ipHeader) const noexcept |
a61dd3f3 | 944 | { |
a61dd3f3 | 945 | // ip options is not supported !!! |
677f9d2b | 946 | const auto l4Length = static_cast<uint16_t>(getDataSize() + sizeof(udphdr)); |
2a5e9760 RG |
947 | auto sum = tcp_udp_v4_header_checksum_partial(ipHeader->saddr, ipHeader->daddr, ipHeader->protocol, l4Length); |
948 | // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) | |
677f9d2b | 949 | sum = ip_checksum_partial(frame + getL4HeaderOffset(), l4Length, sum); |
a61dd3f3 Y |
950 | return ip_checksum_fold(sum); |
951 | } | |
677f9d2b RG |
952 | |
953 | [[nodiscard]] __be16 XskPacket::tcp_udp_v6_checksum(const struct ipv6hdr* ipv6) const noexcept | |
a61dd3f3 | 954 | { |
677f9d2b | 955 | const auto l4Length = static_cast<uint16_t>(getDataSize() + sizeof(udphdr)); |
a61dd3f3 | 956 | uint64_t sum = tcp_udp_v6_header_checksum_partial(&ipv6->saddr, &ipv6->daddr, ipv6->nexthdr, l4Length); |
2a5e9760 | 957 | // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) |
677f9d2b | 958 | sum = ip_checksum_partial(frame + getL4HeaderOffset(), l4Length, sum); |
a61dd3f3 Y |
959 | return ip_checksum_fold(sum); |
960 | } | |
961 | ||
677f9d2b | 962 | [[nodiscard]] uint64_t XskPacket::ip_checksum_partial(const void* ptr, const size_t len, uint64_t sum) noexcept |
a61dd3f3 | 963 | { |
677f9d2b RG |
964 | size_t position{0}; |
965 | /* Main loop: 32 bits at a time */ | |
966 | for (position = 0; position < len; position += sizeof(uint32_t)) { | |
967 | uint32_t value{}; | |
2a5e9760 RG |
968 | // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) |
969 | memcpy(&value, static_cast<const uint8_t*>(ptr) + position, sizeof(value)); | |
677f9d2b RG |
970 | sum += value; |
971 | } | |
a61dd3f3 Y |
972 | |
973 | /* Handle un-32bit-aligned trailing bytes */ | |
677f9d2b RG |
974 | if ((len - position) >= 2) { |
975 | uint16_t value{}; | |
2a5e9760 RG |
976 | // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) |
977 | memcpy(&value, static_cast<const uint8_t*>(ptr) + position, sizeof(value)); | |
677f9d2b RG |
978 | sum += value; |
979 | position += sizeof(value); | |
a61dd3f3 | 980 | } |
677f9d2b RG |
981 | |
982 | if ((len - position) > 0) { | |
2a5e9760 RG |
983 | // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) |
984 | const auto* ptr8 = static_cast<const uint8_t*>(ptr) + position; | |
985 | sum += ntohs(*ptr8 << 8); /* RFC says pad last byte */ | |
a61dd3f3 Y |
986 | } |
987 | ||
988 | return sum; | |
989 | } | |
677f9d2b | 990 | |
a61dd3f3 Y |
991 | [[nodiscard]] __be16 XskPacket::ip_checksum_fold(uint64_t sum) noexcept |
992 | { | |
2a5e9760 | 993 | while ((sum & ~0xffffffffULL) != 0U) { |
a61dd3f3 | 994 | sum = (sum >> 32) + (sum & 0xffffffffULL); |
677f9d2b | 995 | } |
2a5e9760 | 996 | while ((sum & 0xffff0000ULL) != 0U) { |
a61dd3f3 | 997 | sum = (sum >> 16) + (sum & 0xffffULL); |
677f9d2b | 998 | } |
a61dd3f3 | 999 | |
677f9d2b | 1000 | return static_cast<__be16>(~sum); |
a61dd3f3 | 1001 | } |
677f9d2b RG |
1002 | |
1003 | #ifndef __packed | |
2a5e9760 RG |
1004 | #define packed_attribute __attribute__((packed)) |
1005 | #else | |
1006 | #define packed_attribute __packed | |
677f9d2b RG |
1007 | #endif |
1008 | ||
2a5e9760 | 1009 | // NOLINTNEXTLINE(bugprone-easily-swappable-parameters) |
a61dd3f3 Y |
1010 | [[nodiscard]] uint64_t XskPacket::tcp_udp_v4_header_checksum_partial(__be32 src_ip, __be32 dst_ip, uint8_t protocol, uint16_t len) noexcept |
1011 | { | |
1012 | struct header | |
1013 | { | |
1014 | __be32 src_ip; | |
1015 | __be32 dst_ip; | |
1016 | __uint8_t mbz; | |
1017 | __uint8_t protocol; | |
1018 | __be16 length; | |
1019 | }; | |
1020 | /* The IPv4 pseudo-header is defined in RFC 793, Section 3.1. */ | |
1021 | struct ipv4_pseudo_header_t | |
1022 | { | |
1023 | /* We use a union here to avoid aliasing issues with gcc -O2 */ | |
1024 | union | |
1025 | { | |
2a5e9760 RG |
1026 | header packed_attribute fields; |
1027 | // NOLINTNEXTLINE(cppcoreguidelines-avoid-c-arrays,modernize-avoid-c-arrays) | |
a61dd3f3 Y |
1028 | uint32_t words[3]; |
1029 | }; | |
1030 | }; | |
1c9b2109 | 1031 | ipv4_pseudo_header_t pseudo_header{}; |
4dd6b4b4 | 1032 | static_assert(sizeof(pseudo_header) == 12, "IPv4 pseudo-header size is incorrect"); |
a61dd3f3 Y |
1033 | |
1034 | /* Fill in the pseudo-header. */ | |
1035 | pseudo_header.fields.src_ip = src_ip; | |
1036 | pseudo_header.fields.dst_ip = dst_ip; | |
1037 | pseudo_header.fields.mbz = 0; | |
1038 | pseudo_header.fields.protocol = protocol; | |
1039 | pseudo_header.fields.length = htons(len); | |
1040 | return ip_checksum_partial(&pseudo_header, sizeof(pseudo_header), 0); | |
1041 | } | |
677f9d2b | 1042 | |
a61dd3f3 Y |
1043 | [[nodiscard]] uint64_t XskPacket::tcp_udp_v6_header_checksum_partial(const struct in6_addr* src_ip, const struct in6_addr* dst_ip, uint8_t protocol, uint32_t len) noexcept |
1044 | { | |
1045 | struct header | |
1046 | { | |
1047 | struct in6_addr src_ip; | |
1048 | struct in6_addr dst_ip; | |
1049 | __be32 length; | |
2a5e9760 | 1050 | // NOLINTNEXTLINE(cppcoreguidelines-avoid-c-arrays,modernize-avoid-c-arrays) |
a61dd3f3 Y |
1051 | __uint8_t mbz[3]; |
1052 | __uint8_t next_header; | |
1053 | }; | |
1054 | /* The IPv6 pseudo-header is defined in RFC 2460, Section 8.1. */ | |
1055 | struct ipv6_pseudo_header_t | |
1056 | { | |
1057 | /* We use a union here to avoid aliasing issues with gcc -O2 */ | |
1058 | union | |
1059 | { | |
2a5e9760 RG |
1060 | header packed_attribute fields; |
1061 | // NOLINTNEXTLINE(cppcoreguidelines-avoid-c-arrays,modernize-avoid-c-arrays) | |
a61dd3f3 Y |
1062 | uint32_t words[10]; |
1063 | }; | |
1064 | }; | |
1c9b2109 | 1065 | ipv6_pseudo_header_t pseudo_header{}; |
4dd6b4b4 | 1066 | static_assert(sizeof(pseudo_header) == 40, "IPv6 pseudo-header size is incorrect"); |
a61dd3f3 Y |
1067 | |
1068 | /* Fill in the pseudo-header. */ | |
1069 | pseudo_header.fields.src_ip = *src_ip; | |
1070 | pseudo_header.fields.dst_ip = *dst_ip; | |
1071 | pseudo_header.fields.length = htonl(len); | |
2a5e9760 | 1072 | // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-array-to-pointer-decay) |
a61dd3f3 Y |
1073 | memset(pseudo_header.fields.mbz, 0, sizeof(pseudo_header.fields.mbz)); |
1074 | pseudo_header.fields.next_header = protocol; | |
1075 | return ip_checksum_partial(&pseudo_header, sizeof(pseudo_header), 0); | |
1076 | } | |
677f9d2b | 1077 | |
ae61f00a | 1078 | void XskPacket::setHeader(PacketBuffer& buf) |
a61dd3f3 Y |
1079 | { |
1080 | memcpy(frame, buf.data(), buf.size()); | |
677f9d2b | 1081 | frameLength = buf.size(); |
ae61f00a | 1082 | buf.clear(); |
a61dd3f3 | 1083 | flags = 0; |
677f9d2b | 1084 | if (!parse(true)) { |
4dd6b4b4 RG |
1085 | throw std::runtime_error("Error setting the XSK frame header"); |
1086 | } | |
a61dd3f3 | 1087 | } |
677f9d2b | 1088 | |
a8c3569c | 1089 | PacketBuffer XskPacket::cloneHeaderToPacketBuffer() const |
a61dd3f3 | 1090 | { |
677f9d2b | 1091 | const auto size = getFrameLen() - getDataSize(); |
ae61f00a RG |
1092 | PacketBuffer tmp(size); |
1093 | memcpy(tmp.data(), frame, size); | |
a61dd3f3 Y |
1094 | return tmp; |
1095 | } | |
677f9d2b | 1096 | |
a61dd3f3 Y |
1097 | int XskWorker::createEventfd() |
1098 | { | |
2a5e9760 RG |
1099 | auto desc = ::eventfd(0, EFD_CLOEXEC); |
1100 | if (desc < 0) { | |
a61dd3f3 Y |
1101 | throw runtime_error("Unable create eventfd"); |
1102 | } | |
2a5e9760 | 1103 | return desc; |
a61dd3f3 | 1104 | } |
677f9d2b | 1105 | |
2a5e9760 | 1106 | void XskWorker::waitForXskSocket() const noexcept |
a61dd3f3 | 1107 | { |
cce416f2 | 1108 | uint64_t value = read(workerWaker, &value, sizeof(value)); |
a61dd3f3 | 1109 | } |
677f9d2b | 1110 | |
2a5e9760 | 1111 | void XskWorker::notifyXskSocket() const |
a61dd3f3 Y |
1112 | { |
1113 | notify(xskSocketWaker); | |
1114 | } | |
1115 | ||
1116 | std::shared_ptr<XskWorker> XskWorker::create() | |
1117 | { | |
1118 | return std::make_shared<XskWorker>(); | |
1119 | } | |
677f9d2b | 1120 | |
f140f2b8 | 1121 | void XskSocket::addWorker(std::shared_ptr<XskWorker> worker) |
a61dd3f3 | 1122 | { |
f140f2b8 RG |
1123 | const auto socketWaker = worker->xskSocketWaker.getHandle(); |
1124 | worker->umemBufBase = umem.bufBase; | |
1125 | d_workers.insert({socketWaker, std::move(worker)}); | |
a61dd3f3 Y |
1126 | fds.push_back(pollfd{ |
1127 | .fd = socketWaker, | |
1128 | .events = POLLIN, | |
1129 | .revents = 0}); | |
1130 | }; | |
677f9d2b | 1131 | |
f140f2b8 RG |
1132 | void XskSocket::addWorkerRoute(const std::shared_ptr<XskWorker>& worker, const ComboAddress& dest) |
1133 | { | |
1134 | d_workerRoutes.lock()->insert({dest, worker}); | |
1135 | } | |
1136 | ||
1137 | void XskSocket::removeWorkerRoute(const ComboAddress& dest) | |
1138 | { | |
1139 | d_workerRoutes.lock()->erase(dest); | |
1140 | } | |
1141 | ||
ea247879 | 1142 | uint64_t XskWorker::frameOffset(const XskPacket& packet) const noexcept |
a61dd3f3 | 1143 | { |
ea247879 | 1144 | return packet.getFrameOffsetFrom(umemBufBase); |
a61dd3f3 | 1145 | } |
677f9d2b | 1146 | |
cce416f2 | 1147 | void XskWorker::notifyWorker() const |
a61dd3f3 Y |
1148 | { |
1149 | notify(workerWaker); | |
1150 | } | |
677f9d2b | 1151 | |
a61dd3f3 Y |
1152 | void XskSocket::getMACFromIfName() |
1153 | { | |
ce2e1328 | 1154 | ifreq ifr{}; |
2a5e9760 RG |
1155 | auto desc = FDWrapper(::socket(AF_INET, SOCK_DGRAM, 0)); |
1156 | if (desc < 0) { | |
ce2e1328 RG |
1157 | throw std::runtime_error("Error creating a socket to get the MAC address of interface " + ifName); |
1158 | } | |
1159 | ||
1160 | if (ifName.size() >= IFNAMSIZ) { | |
1161 | throw std::runtime_error("Unable to get MAC address for interface " + ifName + ": name too long"); | |
1162 | } | |
1163 | ||
cce416f2 | 1164 | // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-array-to-pointer-decay) |
a61dd3f3 | 1165 | strncpy(ifr.ifr_name, ifName.c_str(), ifName.length() + 1); |
2a5e9760 | 1166 | if (ioctl(desc.getHandle(), SIOCGIFHWADDR, &ifr) < 0 || ifr.ifr_hwaddr.sa_family != ARPHRD_ETHER) { |
ce2e1328 | 1167 | throw std::runtime_error("Error getting MAC address for interface " + ifName); |
a61dd3f3 | 1168 | } |
ce2e1328 | 1169 | static_assert(sizeof(ifr.ifr_hwaddr.sa_data) >= std::tuple_size<decltype(source)>{}, "The size of an ARPHRD_ETHER MAC address is smaller than expected"); |
cce416f2 | 1170 | // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-array-to-pointer-decay) |
ce2e1328 | 1171 | memcpy(source.data(), ifr.ifr_hwaddr.sa_data, source.size()); |
a61dd3f3 | 1172 | } |
677f9d2b | 1173 | |
2a5e9760 | 1174 | [[nodiscard]] int XskSocket::timeDifference(const timespec& lhs, const timespec& rhs) noexcept |
a61dd3f3 | 1175 | { |
2a5e9760 | 1176 | const auto res = lhs.tv_sec * 1000 + lhs.tv_nsec / 1000000L - (rhs.tv_sec * 1000 + rhs.tv_nsec / 1000000L); |
a61dd3f3 Y |
1177 | return static_cast<int>(res); |
1178 | } | |
677f9d2b | 1179 | |
2a5e9760 | 1180 | void XskWorker::cleanWorkerNotification() const noexcept |
a61dd3f3 | 1181 | { |
2a5e9760 | 1182 | uint64_t value = read(xskSocketWaker, &value, sizeof(value)); |
a61dd3f3 | 1183 | } |
677f9d2b | 1184 | |
2a5e9760 | 1185 | void XskWorker::cleanSocketNotification() const noexcept |
a61dd3f3 | 1186 | { |
2a5e9760 | 1187 | uint64_t value = read(workerWaker, &value, sizeof(value)); |
a61dd3f3 | 1188 | } |
677f9d2b | 1189 | |
a61dd3f3 Y |
1190 | std::vector<pollfd> getPollFdsForWorker(XskWorker& info) |
1191 | { | |
1192 | std::vector<pollfd> fds; | |
1193 | int timerfd = timerfd_create(CLOCK_MONOTONIC, TFD_CLOEXEC); | |
1194 | if (timerfd < 0) { | |
1195 | throw std::runtime_error("create_timerfd failed"); | |
1196 | } | |
1197 | fds.push_back(pollfd{ | |
1198 | .fd = info.workerWaker, | |
1199 | .events = POLLIN, | |
1200 | .revents = 0, | |
1201 | }); | |
1202 | fds.push_back(pollfd{ | |
1203 | .fd = timerfd, | |
1204 | .events = POLLIN, | |
1205 | .revents = 0, | |
1206 | }); | |
1207 | return fds; | |
1208 | } | |
677f9d2b | 1209 | |
a61dd3f3 Y |
1210 | void XskWorker::fillUniqueEmptyOffset() |
1211 | { | |
1212 | auto frames = sharedEmptyFrameOffset->lock(); | |
1213 | const auto moveSize = std::min(static_cast<size_t>(32), frames->size()); | |
1214 | if (moveSize > 0) { | |
cce416f2 | 1215 | // NOLINTNEXTLINE(bugprone-narrowing-conversions,cppcoreguidelines-narrowing-conversions) |
a61dd3f3 | 1216 | uniqueEmptyFrameOffset.insert(uniqueEmptyFrameOffset.end(), std::make_move_iterator(frames->end() - moveSize), std::make_move_iterator(frames->end())); |
677f9d2b | 1217 | frames->resize(frames->size() - moveSize); |
a61dd3f3 Y |
1218 | } |
1219 | } | |
677f9d2b | 1220 | |
ae61f00a | 1221 | std::optional<XskPacket> XskWorker::getEmptyFrame() |
a61dd3f3 Y |
1222 | { |
1223 | if (!uniqueEmptyFrameOffset.empty()) { | |
1224 | auto offset = uniqueEmptyFrameOffset.back(); | |
1225 | uniqueEmptyFrameOffset.pop_back(); | |
2a5e9760 | 1226 | // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) |
ae61f00a | 1227 | return XskPacket(offset + umemBufBase, 0, frameSize); |
a61dd3f3 Y |
1228 | } |
1229 | fillUniqueEmptyOffset(); | |
1230 | if (!uniqueEmptyFrameOffset.empty()) { | |
1231 | auto offset = uniqueEmptyFrameOffset.back(); | |
1232 | uniqueEmptyFrameOffset.pop_back(); | |
2a5e9760 | 1233 | // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) |
ae61f00a | 1234 | return XskPacket(offset + umemBufBase, 0, frameSize); |
a61dd3f3 | 1235 | } |
ae61f00a | 1236 | return std::nullopt; |
a61dd3f3 | 1237 | } |
677f9d2b | 1238 | |
2a5e9760 | 1239 | void XskWorker::markAsFree(const XskPacket& packet) |
677f9d2b | 1240 | { |
ae61f00a | 1241 | auto offset = frameOffset(packet); |
677f9d2b RG |
1242 | #ifdef DEBUG_UMEM |
1243 | checkUmemIntegrity(__PRETTY_FUNCTION__, __LINE__, offset, {UmemEntryStatus::Status::Received, UmemEntryStatus::Status::TXQueue}, UmemEntryStatus::Status::Free); | |
1244 | #endif /* DEBUG_UMEM */ | |
1245 | uniqueEmptyFrameOffset.push_back(offset); | |
677f9d2b RG |
1246 | } |
1247 | ||
a61dd3f3 Y |
1248 | uint32_t XskPacket::getFlags() const noexcept |
1249 | { | |
1250 | return flags; | |
1251 | } | |
677f9d2b | 1252 | |
a61dd3f3 Y |
1253 | void XskPacket::updatePacket() noexcept |
1254 | { | |
2a5e9760 | 1255 | if ((flags & UPDATE) == 0U) { |
a61dd3f3 Y |
1256 | return; |
1257 | } | |
2a5e9760 | 1258 | if ((flags & REWRITE) == 0U) { |
a61dd3f3 Y |
1259 | changeDirectAndUpdateChecksum(); |
1260 | } | |
1261 | } | |
1262 | #endif /* HAVE_XSK */ |