Skip to content

Commit ae04f0b

Browse files
lib/libposix/sys/socket: add support for sendmsg() to send multiple packets in one call
1 parent c5961eb commit ae04f0b

File tree

8 files changed

+186
-34
lines changed

8 files changed

+186
-34
lines changed

lib/libnet/NetworkClient.h

+16-2
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,9 @@ class NetworkClient
5151
enum SocketAction
5252
{
5353
Connect,
54-
Listen
54+
Listen,
55+
SendSingle,
56+
SendMultiple
5557
};
5658

5759
/**
@@ -64,10 +66,22 @@ class NetworkClient
6466
{
6567
IPV4::Address address;
6668
u16 port;
67-
SocketAction action;
69+
u16 action;
6870
}
6971
SocketInfo;
7072

73+
/**
74+
* Describes a single packet.
75+
*
76+
* This structure is used for operations that involve multiple packets,
77+
* for example: SendMultiple.
78+
*/
79+
struct PacketInfo
80+
{
81+
Address address;
82+
Size size;
83+
};
84+
7185
/**
7286
* Socket types
7387
*/

lib/libnet/UDP.cpp

+14-17
Original file line numberDiff line numberDiff line change
@@ -135,23 +135,20 @@ FileSystem::Result UDP::process(const NetworkQueue::Packet *pkt,
135135
}
136136

137137
FileSystem::Result UDP::sendPacket(const NetworkClient::SocketInfo *src,
138+
const NetworkClient::SocketInfo *dest,
138139
IOBuffer & buffer,
139-
const Size size)
140+
const Size size,
141+
const Size offset)
140142
{
141-
NetworkClient::SocketInfo dest;
142143
NetworkQueue::Packet *pkt;
143144
Header *hdr;
144145

145-
DEBUG("");
146-
147-
// Read destination
148-
buffer.read(&dest, sizeof(dest));
149-
DEBUG("send payload to: ipAddr = " << *IPV4::toString(dest.address) <<
150-
" port = " << dest.port << " size = " << size);
146+
DEBUG("address = " << *IPV4::toString(dest->address) <<
147+
" port = " << dest->port << " size = " << size);
151148

152149
// Get a fresh packet
153-
const FileSystem::Result result = m_parent.getTransmitPacket(&pkt, &dest.address, sizeof(dest.address),
154-
NetworkProtocol::UDP, sizeof(Header) + size - sizeof(dest));
150+
const FileSystem::Result result = m_parent.getTransmitPacket(&pkt, &dest->address, sizeof(dest->address),
151+
NetworkProtocol::UDP, sizeof(Header) + size);
155152
if (result != FileSystem::Success)
156153
{
157154
if (result != FileSystem::RetryAgain)
@@ -164,25 +161,25 @@ FileSystem::Result UDP::sendPacket(const NetworkClient::SocketInfo *src,
164161
// Fill UDP header
165162
hdr = (Header *) (pkt->data + pkt->size);
166163
writeBe16(&hdr->sourcePort, src->port);
167-
writeBe16(&hdr->destPort, dest.port);
168-
writeBe16(&hdr->length, size - sizeof(dest) + sizeof(Header));
164+
writeBe16(&hdr->destPort, dest->port);
165+
writeBe16(&hdr->length, size + sizeof(Header));
169166
writeBe16(&hdr->checksum, 0);
170167

171-
// Insert payload. The payload is just after the 'dest' struct in the IOBuffer.
168+
// Insert payload. The payload is read from the given offset in the IOBuffer.
172169
// Note that the payload must not overwrite past the packet buffer
173170
const Size maximum = getMaximumPacketSize();
174-
const Size needed = pkt->size + size - sizeof(dest);
171+
const Size needed = pkt->size + size;
175172

176173
buffer.read(pkt->data + pkt->size + sizeof(Header),
177-
needed > maximum ? maximum : needed, sizeof(dest));
174+
needed > maximum ? maximum : needed, offset);
178175

179176
// Calculate final checksum
180177
write16(&hdr->checksum, checksum((IPV4::Header *)(pkt->data + pkt->size - sizeof(IPV4::Header)),
181-
hdr, size - sizeof(dest)));
178+
hdr, size));
182179
DEBUG("checksum = " << (uint) hdr->checksum);
183180

184181
// Increment packet size
185-
pkt->size += sizeof(Header) + size - sizeof(dest);
182+
pkt->size += sizeof(Header) + size;
186183

187184
// Transmit now
188185
return m_device.transmit(pkt);

lib/libnet/UDP.h

+4-2
Original file line numberDiff line numberDiff line change
@@ -125,9 +125,11 @@ class UDP : public NetworkProtocol
125125
*
126126
* @return Result code
127127
*/
128-
FileSystem::Result sendPacket(const NetworkClient::SocketInfo *info,
128+
FileSystem::Result sendPacket(const NetworkClient::SocketInfo *src,
129+
const NetworkClient::SocketInfo *dest,
129130
IOBuffer & buffer,
130-
const Size size);
131+
const Size size,
132+
const Size offset);
131133

132134
/**
133135
* Calculate ICMP checksum

lib/libnet/UDPSocket.cpp

+64-13
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
* along with this program. If not, see <http://www.gnu.org/licenses/>.
1616
*/
1717

18+
#include <FreeNOS/System.h>
1819
#include <ByteOrder.h>
1920
#include <Randomizer.h>
2021
#include "Ethernet.h"
@@ -79,24 +80,74 @@ FileSystem::Result UDPSocket::write(IOBuffer & buffer,
7980
{
8081
DEBUG("");
8182

82-
// Receive socket information first?
83-
if (!m_info.port)
84-
{
85-
buffer.read(&m_info, sizeof(m_info));
83+
// Read socket info and action
84+
NetworkClient::SocketInfo dest;
85+
buffer.read(&dest, sizeof(dest));
8686

87-
if (m_info.port == 0)
87+
// Handle the socket operation
88+
switch (dest.action)
89+
{
90+
case NetworkClient::Listen:
8891
{
89-
Randomizer rand;
90-
m_info.port = rand.next() % 65535;
92+
MemoryBlock::copy(&m_info, &dest, sizeof(m_info));
93+
94+
if (m_info.port == 0)
95+
{
96+
Randomizer rand;
97+
m_info.port = rand.next() % 65535;
98+
}
99+
100+
DEBUG("addr =" << m_info.address << " port = " << m_info.port);
101+
return m_udp->bind(this, m_info.port);
91102
}
92103

93-
DEBUG("addr =" << m_info.address << " port = " << m_info.port);
104+
case NetworkClient::SendSingle:
105+
return m_udp->sendPacket(&m_info, &dest, buffer, size - sizeof(dest), sizeof(dest));
94106

95-
return m_udp->bind(this, m_info.port);
96-
}
97-
else
98-
{
99-
return m_udp->sendPacket(&m_info, buffer, size);
107+
case NetworkClient::SendMultiple:
108+
{
109+
NetworkClient::PacketInfo packetInfo;
110+
FileSystemMessage msg;
111+
IOBuffer io;
112+
Size packetOffset = 0;
113+
114+
// Read the first packet info to find the base address for all packets.
115+
//
116+
// Note that it is assumed here that all packet buffers
117+
// originate from the same base address and that each new packet
118+
// starts after NetworkQueue::PayloadBufferSize bytes.
119+
buffer.read(&packetInfo, sizeof(packetInfo), sizeof(dest));
120+
121+
// Prepare dummy filesystem message for the I/O buffer
122+
msg.from = buffer.getMessage()->from;
123+
msg.action = FileSystem::WriteFile;
124+
msg.buffer = (char *)packetInfo.address;
125+
msg.size = NetworkQueue::MaxPackets * PAGESIZE;
126+
io.setMessage(&msg);
127+
128+
// read the array of PacketInfo structs that describe
129+
// all the packets that need to be transferred
130+
for (Size i = sizeof(dest); i < size; i += sizeof(NetworkClient::PacketInfo))
131+
{
132+
buffer.read(&packetInfo, sizeof(packetInfo), i);
133+
DEBUG("packet[" << ((i - sizeof(dest)) / sizeof(NetworkClient::PacketInfo)) <<
134+
"] size = " << packetInfo.size << " offset = " << packetOffset);
135+
136+
const FileSystem::Result r = m_udp->sendPacket(&m_info, &dest, io, packetInfo.size, packetOffset);
137+
if (r != FileSystem::Success)
138+
{
139+
ERROR("failed to send packet: result = " << (int) r);
140+
return r;
141+
}
142+
143+
packetOffset += NetworkQueue::PayloadBufferSize;
144+
}
145+
146+
return FileSystem::Success;
147+
}
148+
149+
default:
150+
return FileSystem::NotSupported;
100151
}
101152
}
102153

lib/libposix/sys/socket.h

+34
Original file line numberDiff line numberDiff line change
@@ -38,8 +38,31 @@ struct sockaddr
3838
u16 port;
3939
};
4040

41+
/**
42+
* Input/Output vector for multi-packet operations
43+
*/
44+
struct iovec
45+
{
46+
void *iov_base;
47+
size_t iov_len;
48+
};
49+
4150
typedef Size socklen_t;
4251

52+
/**
53+
* Describes one or more datagrams
54+
*/
55+
struct msghdr
56+
{
57+
void *msg_name;
58+
socklen_t msg_namelen;
59+
struct iovec *msg_iov;
60+
size_t msg_iovlen;
61+
void *msg_control;
62+
size_t msg_controllen;
63+
int msg_flags;
64+
};
65+
4366
/**
4467
* Connect a socket to an address/port.
4568
*
@@ -81,6 +104,17 @@ extern C int recvfrom(int sockfd, void *buf, size_t len, int flags,
81104
extern C int sendto(int sockfd, const void *buf, size_t len, int flags,
82105
const struct sockaddr *addr, socklen_t addrlen);
83106

107+
/**
108+
* Send multiple datagrams to a remote host.
109+
*
110+
* @param sockfd Socket file descriptor
111+
* @param msg Pointer to the messages to send
112+
* @param flags Optional flags for the send operation
113+
*
114+
* @return Number of bytes send on success and -1 on error
115+
*/
116+
extern C int sendmsg(int sockfd, const struct msghdr *msg, int flags);
117+
84118
/**
85119
* @}
86120
* @}

lib/libposix/sys/socket/sendmsg.cpp

+52
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
/*
2+
* Copyright (C) 2021 Niek Linnenbank
3+
*
4+
* This program is free software: you can redistribute it and/or modify
5+
* it under the terms of the GNU General Public License as published by
6+
* the Free Software Foundation, either version 3 of the License, or
7+
* (at your option) any later version.
8+
*
9+
* This program is distributed in the hope that it will be useful,
10+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
11+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12+
* GNU General Public License for more details.
13+
*
14+
* You should have received a copy of the GNU General Public License
15+
* along with this program. If not, see <http://www.gnu.org/licenses/>.
16+
*/
17+
18+
#include <NetworkClient.h>
19+
#include <stdlib.h>
20+
#include <unistd.h>
21+
#include <sys/socket.h>
22+
#include <errno.h>
23+
24+
extern C int sendmsg(int sockfd, const struct msghdr *msg, int flags)
25+
{
26+
static u8 buf[1024];
27+
NetworkClient::SocketInfo *info = (NetworkClient::SocketInfo *) buf;
28+
NetworkClient::PacketInfo *pkt = (NetworkClient::PacketInfo *) (info + 1);
29+
const struct sockaddr *addr = (const struct sockaddr *) msg->msg_name;
30+
Size bytes = sizeof(*info);
31+
32+
if (msg->msg_namelen != sizeof(struct sockaddr))
33+
{
34+
return ERANGE;
35+
}
36+
37+
info->address = addr->addr;
38+
info->port = addr->port;
39+
info->action = NetworkClient::SendMultiple;
40+
41+
// Prepare the array of NetworkClient::PacketInfo structs
42+
for (Size i = 0; i < msg->msg_iovlen && bytes < sizeof(buf); i++)
43+
{
44+
pkt->address = (Address) msg->msg_iov[i].iov_base;
45+
pkt->size = msg->msg_iov[i].iov_len;
46+
47+
bytes += sizeof(*pkt);
48+
pkt++;
49+
}
50+
51+
return ::write(sockfd, buf, bytes);
52+
}

lib/libposix/sys/socket/sendto.cpp

+1
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ extern C int sendto(int sockfd, const void *buf, size_t len, int flags,
3333

3434
info.address = addr->addr;
3535
info.port = addr->port;
36+
info.action = NetworkClient::SendSingle;
3637

3738
memcpy(packet, &info, sizeof(info));
3839
memcpy(packet + sizeof(info), buf, len);

test/server/network/loopback/LoopbackTest.cpp

+1
Original file line numberDiff line numberDiff line change
@@ -202,6 +202,7 @@ TestCase(LoopbackUdpPing)
202202
// Fill the packet contents
203203
info->address = IPV4::toAddress("127.0.0.1");
204204
info->port = 12345;
205+
info->action = NetworkClient::SendSingle;
205206
MemoryBlock::copy(info + 1, payload, String::length(payload));
206207

207208
// Process the message two times: first for ARP lookup, second for actual packet

0 commit comments

Comments
 (0)