@@ -7,33 +7,37 @@ Distributed under the MIT License (https://opensource.org/licenses/MIT)
77
88#pragma once
99
10- #include < fcntl.h>
11- #include < linux/if_tun.h>
12- // <linux/virtio_net.h> declares a struct member named `class`, valid in C
13- // but a reserved word in C++. Rename it for the duration of the include so
14- // we can still use the system UAPI header (struct virtio_net_hdr et al).
15- #define class class_
16- #include < linux/virtio_net.h>
17- #undef class
18- #include < net/if.h>
19- #include < netinet/in.h>
20- #include < sys/ioctl.h>
21- #include < sys/socket.h>
22- #include < sys/uio.h>
23- #include < unistd.h>
10+ #include < fcntl.h> // NOLINT(build/include_order)
11+ #include < linux/if_tun.h> // NOLINT(build/include_order)
12+ #include < unistd.h> // NOLINT(build/include_order)
2413
14+ #if defined(__clang__)
15+ #pragma clang diagnostic push
16+ #pragma clang diagnostic ignored "-Wkeyword-macro"
17+ #endif
18+ #define class class_
19+ #if defined(__clang__)
20+ #pragma clang diagnostic pop
21+ #endif
2522#include < arpa/inet.h>
23+ #include < linux/ipv6.h> // NOLINT(build/include_order)
24+ #include < linux/virtio_net.h> // NOLINT(build/include_order)
25+ #include < net/if.h> // NOLINT(build/include_order)
26+ #include < netinet/in.h> // NOLINT(build/include_order)
27+ #include < sys/ioctl.h> // NOLINT(build/include_order)
28+ #include < sys/socket.h> // NOLINT(build/include_order)
29+ #include < sys/uio.h> // NOLINT(build/include_order)
30+ #undef class
2631
2732#include < atomic>
2833#include < cerrno>
2934#include < cstdio>
3035#include < cstring>
36+ #include < deque>
3137#include < string>
3238#include < utility>
3339#include < vector>
3440
35- #include < deque>
36-
3741#include < spdlog/spdlog.h> // NOLINT(build/include_order)
3842
3943#include " common/network/ip_utils.h"
@@ -153,18 +157,11 @@ class LinuxTunDevice {
153157 return false ;
154158 }
155159
156- // struct in6_ifreq from <linux/ipv6.h> (defined locally to avoid
157- // conflicts between kernel and libc headers)
158- struct In6Ifreq {
159- struct in6_addr addr;
160- std::uint32_t prefixlen;
161- int ifindex;
162- } req = {};
163-
164- req.ifindex = static_cast <int >(::if_nametoindex (name_.c_str ()));
165- req.prefixlen = static_cast <std::uint32_t >(prefixlen);
166- if (req.ifindex == 0 ||
167- ::inet_pton (AF_INET6 , addr.c_str(), &req.addr) != 1 ||
160+ struct in6_ifreq req = {};
161+ req.ifr6_ifindex = static_cast <int >(::if_nametoindex (name_.c_str ()));
162+ req.ifr6_prefixlen = static_cast <std::uint32_t >(prefixlen);
163+ if (req.ifr6_ifindex == 0 ||
164+ ::inet_pton (AF_INET6 , addr.c_str(), &req.ifr6_addr) != 1 ||
168165 ::ioctl(sock, SIOCSIFADDR , &req) < 0) {
169166 SPDLOG_WARN (" IPv6 SIOCSIFADDR({}) failed: {}" , addr, strerror (errno));
170167 ::close (sock);
@@ -177,8 +174,8 @@ class LinuxTunDevice {
177174 void SetNonBlocking (bool enabled) {
178175 const int flags = ::fcntl (fd_, F_GETFL , 0 );
179176 if (flags >= 0 ) {
180- ::fcntl (fd_, F_SETFL ,
181- enabled ? (flags | O_NONBLOCK ) : (flags & ~O_NONBLOCK ));
177+ ::fcntl (
178+ fd_, F_SETFL , enabled ? (flags | O_NONBLOCK ) : (flags & ~O_NONBLOCK ));
182179 }
183180 }
184181
@@ -294,7 +291,8 @@ class LinuxTunDevice {
294291 // csum_start is used as the network-header length and hdr.hdr_len from
295292 // the kernel is not trusted (it can be the whole first packet length on
296293 // the FORWARD path).
297- bool SegmentGsoFrame (const std::uint8_t * frame, std::size_t size,
294+ bool SegmentGsoFrame (const std::uint8_t * frame,
295+ std::size_t size,
298296 const struct virtio_net_hdr & hdr) {
299297 // Non-GSO frame: pass through. With VIRTIO_NET_HDR_F_NEEDS_CSUM
300298 // (CHECKSUM_PARTIAL) the kernel stored the pseudo-header sum at the
@@ -311,9 +309,15 @@ class LinuxTunDevice {
311309 const std::uint32_t initial = ReadU16Be (p + csum_at);
312310 p[csum_at] = 0 ;
313311 p[csum_at + 1 ] = 0 ;
314- WriteU16Be (p + csum_at,
315- Rfc1071 (p + hdr.csum_start ,
316- static_cast <int >(size - hdr.csum_start ), initial));
312+ std::uint16_t ck = Rfc1071 (p + hdr.csum_start ,
313+ static_cast <int >(size - hdr.csum_start ), initial);
314+ // RFC 768/8200: a UDP checksum computed as 0 goes on the wire as
315+ // 0xFFFF (proto at byte 9 for IPv4, next-header at byte 6 for IPv6).
316+ const std::uint8_t proto = (p[0 ] >> 4 ) == 6u ? p[6 ] : p[9 ];
317+ if (ck == 0u && proto == IPPROTO_UDP ) {
318+ ck = 0xFFFFu ;
319+ }
320+ WriteU16Be (p + csum_at, ck);
317321 } else {
318322 pending_.emplace_back (frame, frame + size);
319323 }
@@ -436,9 +440,12 @@ class LinuxTunDevice {
436440 // Transport checksum: pseudo-header seed + one pass over L4 data.
437441 tr[hdr.csum_offset ] = 0 ;
438442 tr[hdr.csum_offset + 1 ] = 0 ;
439- WriteU16Be (tr + hdr.csum_offset ,
440- Rfc1071 (tr, static_cast <int >(l4_len),
441- addr_sum + proto + static_cast <std::uint32_t >(l4_len)));
443+ std::uint16_t ck = Rfc1071 (tr, static_cast <int >(l4_len),
444+ addr_sum + proto + static_cast <std::uint32_t >(l4_len));
445+ if (!tcp && ck == 0u ) {
446+ ck = 0xFFFFu ; // RFC 768/8200: UDP puts 0xFFFF on the wire, not 0.
447+ }
448+ WriteU16Be (tr + hdr.csum_offset , ck);
442449
443450 pending_.push_back (std::move (seg));
444451 offset += chunk;
0 commit comments