From c39af624aa1c9f70dac1a9d35bbc054524d09fa6 Mon Sep 17 00:00:00 2001 From: no92 Date: Mon, 8 Jul 2024 13:54:31 +0200 Subject: [PATCH] netserver: implement TCP socket support for SO_BINDTODEVICE --- servers/netserver/src/ip/ip4.cpp | 13 ++++++++---- servers/netserver/src/ip/ip4.hpp | 5 +++-- servers/netserver/src/ip/tcp4.cpp | 35 ++++++++++++++++++++++++++++++- 3 files changed, 46 insertions(+), 7 deletions(-) diff --git a/servers/netserver/src/ip/ip4.cpp b/servers/netserver/src/ip/ip4.cpp index fda735d63..36e30a40b 100644 --- a/servers/netserver/src/ip/ip4.cpp +++ b/servers/netserver/src/ip/ip4.cpp @@ -30,10 +30,13 @@ bool Ip4Router::addRoute(Route r) { return routes.emplace(std::move(r)).second; } -std::optional Ip4Router::resolveRoute(uint32_t ip) { +std::optional Ip4Router::resolveRoute(uint32_t ip, std::shared_ptr link) { for (auto i = routes.begin(); i != routes.end(); i++) { const auto &r = *i; if (r.network.sameNet(ip)) { + if(link && r.link.lock()->index() != link->index()) + continue; + if (r.link.expired()) { i = routes.erase(i); continue; @@ -230,8 +233,8 @@ async::result> Ip4Socket::sendmsg(vo } async::result> -Ip4::targetByRemote(uint32_t remote) { - auto oroute = ip4Router().resolveRoute(remote); +Ip4::targetByRemote(uint32_t remote, std::shared_ptr link) { + auto oroute = ip4Router().resolveRoute(remote, link); if (!oroute) { std::cout << "netserver: net unreachable" << std::endl; co_return std::nullopt; @@ -329,7 +332,9 @@ async::result Ip4::sendFrame(Ip4TargetInfo ti, void Ip4::feedPacket(nic::MacAddress, nic::MacAddress, arch::dma_buffer owner, arch::dma_buffer_view frame, std::weak_ptr link) { - Ip4Packet hdr; + Ip4Packet hdr{}; + hdr.link = link; + if (!hdr.parse(std::move(owner), frame)) { std::cout << "netserver: runt, or otherwise invalid, ip4 frame received" << std::endl; diff --git a/servers/netserver/src/ip/ip4.hpp b/servers/netserver/src/ip/ip4.hpp index 8ce49caf2..016b31ad0 100644 --- a/servers/netserver/src/ip/ip4.hpp +++ b/servers/netserver/src/ip/ip4.hpp @@ -63,7 +63,7 @@ struct Ip4Router { // false if insertion fails bool addRoute(Route r); - std::optional resolveRoute(uint32_t ip); + std::optional resolveRoute(uint32_t ip, std::shared_ptr link = {}); inline const std::set &getRoutes() const { return routes; @@ -106,6 +106,7 @@ class Ip4Packet { } header; static_assert(sizeof(header) == 20, "bad header size"); arch::dma_buffer_view data; + std::weak_ptr link; inline arch::dma_buffer_view payload() const { return data.subview(header.ihl * 4); @@ -140,7 +141,7 @@ struct Ip4 { void setLink(CidrAddress addr, std::weak_ptr link); std::optional findLinkIp(uint32_t ipOnNet, nic::Link *link); - async::result> targetByRemote(uint32_t); + async::result> targetByRemote(uint32_t, std::shared_ptr link = {}); async::result sendFrame(Ip4TargetInfo, void*, size_t, uint16_t); diff --git a/servers/netserver/src/ip/tcp4.cpp b/servers/netserver/src/ip/tcp4.cpp index ba24f48ae..dffeba79c 100644 --- a/servers/netserver/src/ip/tcp4.cpp +++ b/servers/netserver/src/ip/tcp4.cpp @@ -5,6 +5,7 @@ #include #include #include +#include #include #include #include @@ -493,6 +494,32 @@ struct Tcp4Socket { co_return 0; } + static async::result> setSocketOption(void *object, + int layer, int number, std::vector optbuf) { + auto self = static_cast(object); + + if(layer == SOL_SOCKET && number == SO_BINDTODEVICE) { + std::string ifname{optbuf.data()}; + + if(ifname.empty()) { + self->boundInterface_ = {}; + } else { + auto nic = nic::Link::byName(ifname); + + if(!nic) + co_return protocols::fs::Error::illegalArguments; + + self->boundInterface_ = nic; + co_return {}; + } + } + + std::cout << std::format("netserver: unhandled TCP socket setsockopt layer {} number {}\n", + layer, number); + + co_return protocols::fs::Error::invalidProtocolOption; + } + constexpr static protocols::fs::FileOperations ops { .read = &read, .write = &write, @@ -507,6 +534,7 @@ struct Tcp4Socket { .recvMsg = &recvMsg, .sendMsg = &sendMsg, .peername = &peername, + .setSocketOption = &setSocketOption, }; bool bindAvailable(uint32_t ipAddress = INADDR_ANY) { @@ -575,6 +603,8 @@ struct Tcp4Socket { uint64_t outSeq_ = 0; uint64_t hupSeq_ = 1; async::recurring_event pollEvent_; + + std::shared_ptr boundInterface_ = {}; }; async::result Tcp4Socket::flushOutPackets_() { @@ -596,7 +626,7 @@ async::result Tcp4Socket::flushOutPackets_() { localFlushedSn_ = randomSn; // Construct and transmit the initial SYN packet. - auto targetInfo = co_await ip4().targetByRemote(remoteEp_.ipAddress); + auto targetInfo = co_await ip4().targetByRemote(remoteEp_.ipAddress, boundInterface_); if (!targetInfo) { // TODO: Return an error to users. std::cout << "netserver: Destination unreachable" << std::endl; @@ -722,6 +752,9 @@ async::result Tcp4Socket::flushOutPackets_() { } void Tcp4Socket::handleInPacket_(TcpPacket packet) { + if(boundInterface_ && boundInterface_->index() != packet.packet->link.lock()->index()) + return; + if(connectState_ == ConnectState::sendSyn) { if(localSettledSn_ == localFlushedSn_) { std::cout << "netserver: Rejecting packet before SYN is sent [sendSyn]"