1 #include "hmbdc/Copyright.hpp" 5 #define NETMAP_WITH_LIBS 6 #include <net/netmap_user.h> 7 #undef NETMAP_WITH_LIBS 9 #include "hmbdc/tips/netmap/Messages.hpp" 10 #include "hmbdc/tips/netmap/DefaultUserConfig.hpp" 11 #include "hmbdc/app/Base.hpp" 12 #include "hmbdc/pattern/MonoLockFreeBuffer.hpp" 13 #include "hmbdc/comm/eth/Misc.h" 14 #include "hmbdc/time/Rater.hpp" 15 #include "hmbdc/numeric/BitMath.hpp" 18 #include <type_traits> 22 #include <netinet/ether.h> 23 #include <linux/if_packet.h> 24 #include <sys/sysctl.h> 29 namespace hmbdc {
namespace tips {
namespace netmap {
34 namespace sendtransportengine_detail {
35 HMBDC_CLASS_HAS_DECLARE(hmbdc_net_queued_ts);
45 size_t bufferedMessageCount()
const {
46 return buffer_.remainingSize();
49 size_t subscribingPartyDetectedCount(uint16_t tag)
const {
53 template <MessageTupleC Messages,
typename Node>
54 void advertiseFor(
Node const& node, uint16_t mod, uint16_t res) {
76 for (
int i = nmd_->first_tx_ring; i <= nmd_->last_tx_ring; i++) {
77 struct netmap_ring * txring = NETMAP_TXRING(nmd_->nifp, i);
78 if (nm_ring_empty(txring))
83 if (hmbdc_unlikely(ioctl(nmd_->fd, NIOCTXSYNC, NULL) < 0)) {
84 HMBDC_THROW(std::runtime_error,
"IO error");
88 void stoppedCb(std::exception
const& e)
override;
90 char const* hmbdcName()
const {
91 return this->hmbdcName_.c_str();
94 std::tuple<char const*, int> schedSpec()
const {
95 return std::make_tuple(this->schedPolicy_.c_str(), this->schedPriority_);
98 template <
typename Message>
99 void queue(Message&& msg) HMBDC_RESTRICT {
101 auto it = buffer_.claim(n);
102 queue(it, std::forward<Message>(msg));
103 buffer_.commit(it, n);
106 template <
typename Message>
107 bool tryQueue(Message&& msg) HMBDC_RESTRICT {
109 auto it = buffer_.tryClaim(n);
111 queue(it, std::forward<Message>(msg));
112 buffer_.commit(it, n);
118 template <
typename M,
typename... Messages>
119 void queue(pattern::MonoLockFreeBuffer::iterator it, M&& msg, Messages&&... msgs) {
120 using Message =
typename std::decay<M>::type;
121 static_assert(std::is_trivially_destructible<Message>::value,
"cannot send message with dtor");
122 if (hmbdc_unlikely(
sizeof(Message) > maxMessageSize_)) {
123 HMBDC_THROW(std::out_of_range,
"maxMessageSize too small to hold a message when constructing SendTransportEngine");
126 auto tmh = TransportMessageHeader::copyTo(s, std::forward<M>(msg));
128 if constexpr (has_hmbdc_net_queued_ts<Message>::value) {
129 tmh->template wrapped<Message>().hmbdc_net_queued_ts = hmbdc::time::SysTime::now();
131 queue(++it, std::forward<Messages>(msgs)...);
134 template <
typename Message,
typename ... Args>
135 void queueInPlace(Args&&... args) HMBDC_RESTRICT {
136 static_assert(std::is_trivially_destructible<Message>::value,
"cannot send message with dtor");
137 if (hmbdc_unlikely(
sizeof(Message) > maxMessageSize_)) {
138 HMBDC_THROW(std::out_of_range,
"maxMessageSize too small to hold a message when constructing SendTransportEngine");
140 auto s = buffer_.claim();
141 TransportMessageHeader::copyToInPlace<Message>(*s, std::forward<Args>(args)...);
146 void queue(pattern::MonoLockFreeBuffer::iterator it) {}
149 uint16_t outBufferSizePower2();
150 void sendPackets(
struct netmap_ring *);
152 void getMacAddresses();
154 void initializePacket(
hmbdc::comm::eth::pkt *,
int, std::string, std::string, ether_addr, ether_addr, uint16_t, uint16_t);
160 std::string hmbdcName_;
161 std::string schedPolicy_;
163 size_t maxMessageSize_;
166 struct nm_desc *nmd_;
167 pattern::MonoLockFreeBuffer buffer_;
171 ether_addr srcEthAddr_;
172 ether_addr dstEthAddr_;
176 size_t maxSendBatch_;
182 using SendTransportEngine = sendtransportengine_detail::SendTransportEngine;
186 namespace hmbdc {
namespace tips {
namespace netmap {
188 namespace sendtransportengine_detail {
192 SendTransportEngine::
193 outBufferSizePower2() {
194 auto res = config_.
getExt<uint16_t>(
"outBufferSizePower2");
198 res = hmbdc::numeric::log2Upper(16ul * 1024ul / (8ul + maxMessageSize_));
199 HMBDC_LOG_N(
"auto set --outBufferSizePower2=", res);
204 SendTransportEngine::
206 : config_((cfg.setAdditionalFallbackConfig(
hmbdc::app::Config(DefaultUserConfig))
207 , cfg.resetSection(
"tx", false)))
208 , maxMessageSize_(maxMessageSize)
210 , buffer_(maxMessageSize + sizeof(TransportMessageHeader) + sizeof(
hmbdc::app::MessageHead)
211 , outBufferSizePower2())
215 , doChecksum_(config_.getExt<
bool>(
"doChecksum"))
216 , rater_(hmbdc::time::Duration::seconds(1u)
217 , config_.getExt<
size_t>(
"sendBytesPerSec")
218 , config_.getExt<
size_t>(
"sendBytesBurst")
219 , config_.getExt<
size_t>(
"sendBytesBurst") != 0ul
221 , maxSendBatch_(config_.getExt<
size_t>(
"maxSendBatch"))
222 , mtu_(config_.getExt<
size_t>(
"mtu")) {
224 config_(hmbdcName_,
"hmbdcName")
225 (schedPolicy_,
"schedPolicy")
226 (schedPriority_,
"schedPriority")
229 memcpy(&srcEthAddr_, ether_aton(config_.getExt<std::string>(
"srcEthAddr").c_str())
230 ,
sizeof(srcEthAddr_));
231 memcpy(&dstEthAddr_, ether_aton(config_.getExt<std::string>(
"dstEthAddr").c_str())
232 ,
sizeof(dstEthAddr_));
235 struct nmreq baseNmd;
236 bzero(&baseNmd,
sizeof(baseNmd));
237 baseNmd.nr_flags |= NR_ACCEPT_VNET_HDR;
238 config_(baseNmd.nr_tx_slots,
"nmTxSlots");
239 config_(baseNmd.nr_rx_slots,
"nmRxSlots");
240 config_(baseNmd.nr_tx_rings,
"nmTxRings");
241 config_(baseNmd.nr_rx_rings,
"nmRxRings");
243 auto nmport = config_.getExt<std::string>(
"netmapPort");
244 uint32_t flags = config_.getExt<uint32_t>(
"nmOpenFlags");
245 nmd_ = nm_open(nmport.c_str(), &baseNmd, flags, NULL);
247 HMBDC_THROW(std::runtime_error,
"cannot open " << nmport);
250 if (nmd_->first_tx_ring != nmd_->last_tx_ring) {
251 HMBDC_LOG_W(
"multiple tx rings exist on ", nmport,
" the recv side could receive out of order messages." 252 " to avoid it, use more specific netmapPort with the ring number. for example: netmap::p2p1-2");
256 memset(&req, 0,
sizeof(req));
257 bcopy(nmd_->req.nr_name, req.nr_name,
sizeof(req.nr_name));
258 req.nr_version = NETMAP_API;
259 req.nr_cmd = NETMAP_VNET_HDR_GET;
260 int err = ioctl(nmd_->fd, NIOCREGIF, &req);
262 HMBDC_THROW(std::runtime_error,
"Unable to get virtio-net header length");
264 virtHeader_ = req.nr_arg1;
266 initializePacket(&precalculatedPacketHead_
267 , config_.getExt<uint16_t>(
"ttl")
268 , config_.getExt<std::string>(
"srcIp")
269 , config_.getExt<std::string>(
"dstIp")
272 , config_.getExt<uint16_t>(
"srcPort")
273 , config_.getExt<uint16_t>(
"dstPort")
275 sleep(config_.getExt<
int>(
"nmResetWaitSec"));
277 if (hmbdc_unlikely(ioctl(nmd_->fd, NIOCTXSYNC, NULL) < 0)) {
278 HMBDC_THROW(std::runtime_error,
"IO error");
280 for (
int i = nmd_->first_tx_ring; i <= nmd_->last_tx_ring; i++) {
281 struct netmap_ring * txring = NETMAP_TXRING(nmd_->nifp, i);
282 txring->head = txring->cur = txring->tail;
288 SendTransportEngine::
294 SendTransportEngine::
295 ~SendTransportEngine() {
312 HMBDC_LOG_C(e.what());
317 SendTransportEngine::
318 sendPackets(
struct netmap_ring * HMBDC_RESTRICT ring) HMBDC_RESTRICT {
319 uint32_t cur = ring->cur;
320 if (hmbdc_unlikely(cur == ring->tail))
return;
322 auto limit = maxSendBatch_ * ((ring->tail - cur) % ring->num_slots);
323 if (hmbdc_unlikely(!(buffer_.peek(begin, end, limit)))) {
326 bool slotInited =
false;
328 auto batch = maxSendBatch_;
329 struct netmap_slot *slot = &ring->slot[cur];
330 uint32_t slotLen = 0;
331 char *p = NETMAP_BUF(ring, slot->buf_idx);
334 uint16_t slotLenMax = std::min(mtu_, (uint16_t)ring->nr_buf_size);
337 if (hmbdc_likely(rater_.check(th->wireSize()))) {
339 auto wireSize = (uint16_t)(
340 sizeof(ether_header) +
sizeof(ip) +
sizeof(udphdr) + virtHeader_
343 - virtHeader_, wireSize);
347 auto wireSize = th->wireSize();
348 if (slotLen + wireSize <= slotLenMax) {
349 memcpy(p + slotLen, th, (
int)wireSize);
359 size_t wireSizeExcludingHead = slotLen
360 - (
sizeof(ether_header) +
sizeof(ip) +
sizeof(udphdr) + virtHeader_);
361 updatePacket(currentPktPtr, wireSizeExcludingHead, doChecksum_);
362 cur = nm_ring_next(ring, cur);
364 if (cur == ring->tail)
break;
365 slot = &ring->slot[cur];
366 p = NETMAP_BUF(ring, slot->buf_idx);
369 batch = maxSendBatch_;
378 size_t wireSizeExcludingHead = slotLen
379 - (
sizeof(ether_header) +
sizeof(ip) +
sizeof(udphdr) + virtHeader_);
380 updatePacket(currentPktPtr, wireSizeExcludingHead, doChecksum_);
381 cur = nm_ring_next(ring, cur);
384 ring->head = ring->cur = cur;
385 buffer_.wasteAfterPeek(begin, it - begin,
true);
390 SendTransportEngine::
392 auto nmport = config_.
getExt<std::string>(
"netmapPort");
394 if (strncmp(nmport.c_str(),
"vale", 4) == 0)
return;
396 if (nmport.find_first_of(
":") == std::string::npos) {
397 HMBDC_THROW(std::runtime_error
398 ,
"wrong netmapPort format (examples: netmap:eth0, netmap:eth0-0)");
400 auto iface = nmport.substr(nmport.find_first_of(
":"));
401 iface = iface.substr(1, iface.find_first_of(
"-^") - 1);
404 struct ifaddrs *ifaphead, *ifap;
405 int l =
sizeof(ifap->ifa_name);
407 if (getifaddrs(&ifaphead) != 0) {
408 HMBDC_THROW(std::runtime_error,
"getifaddrs failed for" << iface);
410 for (ifap = ifaphead; ifap; ifap = ifap->ifa_next) {
411 struct sockaddr_ll *sll =
412 (
struct sockaddr_ll *)ifap->ifa_addr;
415 if (!sll || sll->sll_family != AF_PACKET)
417 if (strncmp(ifap->ifa_name, iface.c_str(), l) != 0)
419 mac = (uint8_t *)(sll->sll_addr);
421 char srcEthAddrStr[20];
422 sprintf(srcEthAddrStr,
"%02x:%02x:%02x:%02x:%02x:%02x",
423 mac[0], mac[1], mac[2],
424 mac[3], mac[4], mac[5]);
425 memcpy(&srcEthAddr_, ether_aton(srcEthAddrStr),
sizeof(srcEthAddr_));
428 freeifaddrs(ifaphead);
430 HMBDC_THROW(std::runtime_error,
"no local interface named " << iface);
436 SendTransportEngine::
438 , ether_addr srcEthAddr, ether_addr dstEthAddr, uint16_t srcPort, uint16_t dstPort) {
439 struct ether_header *eh;
443 sscanf(srcIpStr.c_str(),
"%d.%d.%d.%d", &a, &b, &c, &d);
444 auto srcIp = (a << 24u) + (b << 16u) + (c << 8u) + d;
445 sscanf(dstIpStr.c_str(),
"%d.%d.%d.%d", &a, &b, &c, &d);
446 auto dstIp = (a << 24u) + (b << 16u) + (c << 8u) + d;
450 bcopy(&srcEthAddr, eh->ether_shost, 6);
451 bcopy(&dstEthAddr, eh->ether_dhost, 6);
453 eh->ether_type = htons(ETHERTYPE_IP);
455 #pragma GCC diagnostic push 456 #if defined __clang__ || __GNUC_PREREQ(9,0) 457 #pragma GCC diagnostic ignored "-Waddress-of-packed-member" 460 udp = &pkt->ipv4.udp;
461 ip->ip_v = IPVERSION;
462 ip->ip_hl =
sizeof(*ip) >> 2;
464 ip->ip_tos = IPTOS_LOWDELAY;
467 ip->ip_off = htons(IP_DF);
469 ip->ip_p = IPPROTO_UDP;
470 ip->ip_dst.s_addr = htonl(dstIp);
471 ip->ip_src.s_addr = htonl(srcIp);
473 ip->ip_len =
sizeof(*ip) +
sizeof(udphdr);
474 udp->source = htons(srcPort);
475 udp->dest = htons(dstPort);
476 udp->len =
sizeof(udphdr);
479 bzero(&pkt->vh,
sizeof(pkt->vh));
484 SendTransportEngine::
486 packet->ipv4.ip.ip_len += payloadWireSize;
487 packet->ipv4.ip.ip_len = ntohs(packet->ipv4.ip.ip_len);
489 packet->ipv4.ip.ip_sum = hmbdc::comm::eth::wrapsum(hmbdc::comm::eth::checksum(
490 &packet->ipv4.ip,
sizeof(packet->ipv4.ip), 0));
493 packet->ipv4.udp.len += payloadWireSize;
494 packet->ipv4.udp.len = htons(packet->ipv4.udp.len);
496 auto udp = &packet->ipv4.udp;
497 packet->ipv4.udp.check = hmbdc::comm::eth::wrapsum(
498 hmbdc::comm::eth::checksum(udp,
sizeof(*udp),
499 hmbdc::comm::eth::checksum(packet->ipv4.body, payloadWireSize,
500 hmbdc::comm::eth::checksum(&packet->ipv4.ip.ip_src, 2 *
sizeof(packet->ipv4.ip.ip_src),
501 IPPROTO_UDP + (u_int32_t)ntohs(udp->len)))));
505 #pragma GCC diagnostic pop T getExt(const path_type ¶m, bool throwIfMissing=true) const
get a value from the config
Definition: Config.hpp:238
void invokedCb(size_t) HMBDC_RESTRICT override
this callback is called all the time (frequently) - the exact timing is after a batch of messages are...
Definition: SendTransportEngine.hpp:75
power a netmap port sending functions
Definition: SendTransportEngine.hpp:42
void stoppedCb(std::exception const &e) override
callback called when this Client is taken out of message dispatching
Definition: SendTransportEngine.hpp:311
void rotate()
if the user choose no to have a Context to manage and run the engine this method can be called from a...
Definition: SendTransportEngine.hpp:65
class to hold an hmbdc configuration
Definition: Config.hpp:44
bool droppedCb() override
callback called after the Client is safely taken out of the Context
Definition: SendTransportEngine.hpp:303
a Node is a thread of execution that can suscribe and receive Messages
Definition: Node.hpp:51
A Client represents a thread of execution/a task. The execution is managed by a Context. a Client object could participate in message dispatching as the receiver of specifed message types.
Definition: Client.hpp:128
Definition: LockFreeBufferMisc.hpp:89