Skip to content

Commit de85041

Browse files
committed
init/aws-nitro: support partial read/write ethernet packet forwarding
Ensure the requested number of bytes is forwarded to/from the host vsock providing the network access and the guest TAP device routing the application network traffic. To write this code I used Cursor and the claude-4.6-opus-high model. Signed-off-by: Jake Correnti <jakecorrenti+github@proton.me>
1 parent 7c5292c commit de85041

1 file changed

Lines changed: 105 additions & 14 deletions

File tree

init/aws-nitro/device/net_tap_afvsock.c

Lines changed: 105 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,58 @@
3131
#define TUN_DEV_MAJOR 10
3232
#define TUN_DEV_MINOR 200
3333

34+
#define ETH_HEADER_LEN 14
35+
36+
/*
37+
* Read exactly n bytes into the buffer, retrying on partial reads.
38+
* Returns n on success, 0 on clean EOF, or -1 on error.
39+
*/
40+
static ssize_t read_exact(int fd, void *buf, size_t n)
41+
{
42+
size_t total = 0;
43+
44+
while (total < n) {
45+
ssize_t r = read(fd, (char *)buf + total, n - total);
46+
if (r < 0) {
47+
if (errno == EINTR)
48+
continue;
49+
return -1;
50+
} else if (r == 0) {
51+
if (total > 0) {
52+
errno = EIO;
53+
return -1;
54+
}
55+
return 0;
56+
}
57+
total += r;
58+
}
59+
return (ssize_t)total;
60+
}
61+
62+
/*
63+
* Write exactly n bytes from the buffer to the fd, retrying on partial writes.
64+
* Returns n on success, or -1 on error.
65+
*/
66+
static ssize_t write_all(int fd, const void *buf, size_t n)
67+
{
68+
size_t total = 0;
69+
70+
while (total < n) {
71+
ssize_t w = write(fd, (const char *)buf + total, n - total);
72+
if (w <= 0) {
73+
if (w < 0 && errno == EINTR)
74+
continue;
75+
76+
if (w == 0)
77+
errno = EIO;
78+
79+
return -1;
80+
}
81+
total += w;
82+
}
83+
return (ssize_t)total;
84+
}
85+
3486
/*
3587
* Forward ethernet packets to/from the host vsock providing network access and
3688
* the guest TAP device routing application network traffic.
@@ -43,12 +95,12 @@ static int tap_vsock_forward(int tun_fd, int vsock_fd, int shutdown_fd,
4395
bool event_found;
4496
struct ifreq ifr;
4597
int ret, sock_fd;
46-
unsigned int sz;
98+
uint32_t sz;
4799
ssize_t nread;
48100

49101
/*
50102
* Fetch the TAP device's Maximum Transfer Unit (MTU) and allocate a buffer
51-
* in that size to transfer ethernet frames to/from the host.
103+
* large enough for a full eth frame (MTU + eth header).
52104
*/
53105
sock_fd = socket(AF_INET, SOCK_DGRAM, 0);
54106
if (sock_fd < 0) {
@@ -68,17 +120,19 @@ static int tap_vsock_forward(int tun_fd, int vsock_fd, int shutdown_fd,
68120

69121
close(sock_fd);
70122

71-
buf = (unsigned char *)malloc(ifr.ifr_mtu);
123+
unsigned int buffer_size = ifr.ifr_mtu + ETH_HEADER_LEN;
124+
buf = (unsigned char *)malloc(buffer_size);
72125
if (buf == NULL) {
73126
perror("allocate buffer for TAP/vsock communication");
74127
exit(-1);
75128
}
76129

77130
// Forward the MTU to the host for it to allocate a corresponding buffer.
78-
ret = write(vsock_fd, (void *)&ifr.ifr_mtu, sizeof(int));
79-
if (ret < sizeof(int)) {
131+
// The host is expecting a 32-bit header.
132+
uint32_t mtu = ifr.ifr_mtu;
133+
if (write_all(vsock_fd, &mtu, sizeof(mtu)) < 0) {
80134
perror("write TAP device MTU to host");
81-
exit(-errno);
135+
exit(EXIT_FAILURE);
82136
}
83137

84138
pfds[0].fd = vsock_fd;
@@ -97,25 +151,62 @@ static int tap_vsock_forward(int tun_fd, int vsock_fd, int shutdown_fd,
97151
event_found = false;
98152
// Event on vsock. Read the frame and write it to the TAP device.
99153
if (pfds[0].revents & POLLIN) {
100-
nread = read(vsock_fd, &sz, 4);
101-
if (nread != 4)
154+
nread = read_exact(vsock_fd, &sz, sizeof(sz));
155+
if (nread < 0) {
156+
perror("failure to read header");
157+
exit(EXIT_FAILURE);
158+
} else if (nread == 0) {
159+
// vsock connection is closed
102160
exit(0);
161+
}
162+
163+
unsigned int len = ntohl(sz);
164+
if (len > buffer_size) {
165+
fprintf(stderr, "frame size %u exceeds buffer %u\n", len,
166+
buffer_size);
167+
exit(EXIT_FAILURE);
168+
}
103169

104-
unsigned int len = htonl(sz);
170+
nread = read_exact(vsock_fd, buf, len);
171+
if (nread != (ssize_t)len) {
172+
if (nread == 0)
173+
errno = EIO;
174+
perror("failure to read from buffer");
175+
exit(EXIT_FAILURE);
176+
}
105177

106-
nread = read(vsock_fd, buf, len);
107-
write(tun_fd, buf, nread);
178+
// TAP device is expected to write exactly one eth frame
179+
ssize_t w;
180+
do {
181+
w = write(tun_fd, buf, nread);
182+
} while (w < 0 && errno == EINTR);
183+
184+
if (w != nread) {
185+
if (w >= 0)
186+
errno = EIO;
187+
perror("failure to write buffer to tap device");
188+
exit(EXIT_FAILURE);
189+
}
108190

109191
event_found = true;
110192
}
111193

112194
// Event on the TAP device. Read the frame and write it to the vsock.
113195
if (pfds[1].revents & POLLIN) {
114-
nread = read(tun_fd, buf, ifr.ifr_mtu);
196+
do {
197+
nread = read(tun_fd, buf, buffer_size);
198+
} while (nread < 0 && errno == EINTR);
199+
115200
if (nread > 0) {
116201
sz = htonl(nread);
117-
write(vsock_fd, (void *)&sz, 4);
118-
write(vsock_fd, buf, nread);
202+
if (write_all(vsock_fd, &sz, sizeof(sz)) < 0) {
203+
perror("unable to write header to vsock");
204+
exit(EXIT_FAILURE);
205+
}
206+
if (write_all(vsock_fd, buf, nread) < 0) {
207+
perror("unable to write buffer to vsock");
208+
exit(EXIT_FAILURE);
209+
}
119210
}
120211

121212
event_found = true;

0 commit comments

Comments
 (0)