From: Marcin Siodelski Date: Wed, 3 Apr 2013 12:24:14 +0000 (+0200) Subject: [991] Moved send/receive implementation to class derived from PktFilter. X-Git-Tag: bind10-1.1.0beta2-release~8^2~30^2~14 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=43fa9db5aecf34e77a211c43c4cc2cc6e27e7530;p=thirdparty%2Fkea.git [991] Moved send/receive implementation to class derived from PktFilter. --- diff --git a/src/lib/dhcp/iface_mgr.cc b/src/lib/dhcp/iface_mgr.cc index 9cf1614db4..1a0848d117 100644 --- a/src/lib/dhcp/iface_mgr.cc +++ b/src/lib/dhcp/iface_mgr.cc @@ -581,7 +581,19 @@ int IfaceMgr::openSocket6(Iface& iface, const IOAddress& addr, uint16_t port) { } } - Iface::SocketInfo info(sock, addr, port); + SocketInfo info(sock, addr, port); + iface.addSocket(info); + + return (sock); +} + +int IfaceMgr::openSocket4(Iface& iface, const IOAddress& addr, uint16_t port, + bool receive_bcast, bool send_bcast) { + + int sock = packet_filter_->openSocket(iface, addr, port, + receive_bcast, send_bcast); + + SocketInfo info(sock, addr, port); iface.addSocket(info); return (sock); @@ -688,6 +700,18 @@ IfaceMgr::send(const Pkt6Ptr& pkt) { return (result); } +bool +IfaceMgr::send(const Pkt4Ptr& pkt) { + + Iface* iface = getIface(pkt->getIface()); + if (!iface) { + isc_throw(BadValue, "Unable to send Pkt4. Invalid interface (" + << pkt->getIface() << ") specified."); + } + + return (packet_filter_->send(getSocket(*pkt), pkt)); +} + boost::shared_ptr IfaceMgr::receive4(uint32_t timeout_sec, uint32_t timeout_usec /* = 0 */) { @@ -696,7 +720,7 @@ IfaceMgr::receive4(uint32_t timeout_sec, uint32_t timeout_usec /* = 0 */) { isc_throw(BadValue, "fractional timeout must be shorter than" " one million microseconds"); } - const Iface::SocketInfo* candidate = 0; + const SocketInfo* candidate = 0; IfaceCollection::const_iterator iface; fd_set sockets; int maxfd = 0; @@ -783,64 +807,7 @@ IfaceMgr::receive4(uint32_t timeout_sec, uint32_t timeout_usec /* = 0 */) { } // Now we have a socket, let's get some data from it! - struct sockaddr_in from_addr; - uint8_t buf[RCVBUFSIZE]; - - memset(&control_buf_[0], 0, control_buf_len_); - memset(&from_addr, 0, sizeof(from_addr)); - - // Initialize our message header structure. - struct msghdr m; - memset(&m, 0, sizeof(m)); - - // Point so we can get the from address. - m.msg_name = &from_addr; - m.msg_namelen = sizeof(from_addr); - - struct iovec v; - v.iov_base = static_cast(buf); - v.iov_len = RCVBUFSIZE; - m.msg_iov = &v; - m.msg_iovlen = 1; - - // Getting the interface is a bit more involved. - // - // We set up some space for a "control message". We have - // previously asked the kernel to give us packet - // information (when we initialized the interface), so we - // should get the destination address from that. - m.msg_control = &control_buf_[0]; - m.msg_controllen = control_buf_len_; - - result = recvmsg(candidate->sockfd_, &m, 0); - if (result < 0) { - isc_throw(SocketReadError, "failed to receive UDP4 data"); - } - - // We have all data let's create Pkt4 object. - Pkt4Ptr pkt = Pkt4Ptr(new Pkt4(buf, result)); - - pkt->updateTimestamp(); - - unsigned int ifindex = iface->getIndex(); - - IOAddress from(htonl(from_addr.sin_addr.s_addr)); - uint16_t from_port = htons(from_addr.sin_port); - - // Set receiving interface based on information, which socket was used to - // receive data. OS-specific info (see os_receive4()) may be more reliable, - // so this value may be overwritten. - pkt->setIndex(ifindex); - pkt->setIface(iface->getName()); - pkt->setRemoteAddr(from); - pkt->setRemotePort(from_port); - pkt->setLocalPort(candidate->port_); - - if (!os_receive4(m, pkt)) { - isc_throw(SocketReadError, "unable to find pktinfo"); - } - - return (pkt); + return (packet_filter_->receive(*iface, *candidate)); } Pkt6Ptr IfaceMgr::receive6(uint32_t timeout_sec, uint32_t timeout_usec /* = 0 */ ) { @@ -850,7 +817,7 @@ Pkt6Ptr IfaceMgr::receive6(uint32_t timeout_sec, uint32_t timeout_usec /* = 0 */ " one million microseconds"); } - const Iface::SocketInfo* candidate = 0; + const SocketInfo* candidate = 0; fd_set sockets; int maxfd = 0; stringstream names; diff --git a/src/lib/dhcp/iface_mgr.h b/src/lib/dhcp/iface_mgr.h index a1e28fd1df..f8fa2ee428 100644 --- a/src/lib/dhcp/iface_mgr.h +++ b/src/lib/dhcp/iface_mgr.h @@ -70,6 +70,24 @@ public: isc::Exception(file, line, what) { }; }; +/// Holds information about socket. +struct SocketInfo { + uint16_t sockfd_; /// socket descriptor + isc::asiolink::IOAddress addr_; /// bound address + uint16_t port_; /// socket port + uint16_t family_; /// IPv4 or IPv6 + + /// @brief SocketInfo constructor. + /// + /// @param sockfd socket descriptor + /// @param addr an address the socket is bound to + /// @param port a port the socket is bound to + SocketInfo(uint16_t sockfd, const isc::asiolink::IOAddress& addr, + uint16_t port) + :sockfd_(sockfd), addr_(addr), port_(port), family_(addr.getFamily()) { } +}; + + /// @brief represents a single network interface /// /// Iface structure represents network interface with all useful @@ -84,23 +102,6 @@ public: /// type that defines list of addresses typedef std::vector AddressCollection; - /// Holds information about socket. - struct SocketInfo { - uint16_t sockfd_; /// socket descriptor - isc::asiolink::IOAddress addr_; /// bound address - uint16_t port_; /// socket port - uint16_t family_; /// IPv4 or IPv6 - - /// @brief SocketInfo constructor. - /// - /// @param sockfd socket descriptor - /// @param addr an address the socket is bound to - /// @param port a port the socket is bound to - SocketInfo(uint16_t sockfd, const isc::asiolink::IOAddress& addr, - uint16_t port) - :sockfd_(sockfd), addr_(addr), port_(port), family_(addr.getFamily()) { } - }; - /// type that holds a list of socket informations /// @todo: Add SocketCollectionConstIter type typedef std::list SocketCollection; diff --git a/src/lib/dhcp/iface_mgr_linux.cc b/src/lib/dhcp/iface_mgr_linux.cc index 1fea884585..cc7c86f2e8 100644 --- a/src/lib/dhcp/iface_mgr_linux.cc +++ b/src/lib/dhcp/iface_mgr_linux.cc @@ -499,18 +499,6 @@ IfaceMgr::isDirectResponseSupported() { return (false); } -int IfaceMgr::openSocket4(Iface& iface, const IOAddress& addr, uint16_t port, - bool receive_bcast, bool send_bcast) { - - int sock = packet_filter_->openSocket(iface, addr, port, - receive_bcast, send_bcast); - - Iface::SocketInfo info(sock, addr, port); - iface.addSocket(info); - - return (sock); -} - /// @brief sets flag_*_ fields. /// /// This implementation is OS-specific as bits have different meaning @@ -527,105 +515,15 @@ void Iface::setFlags(uint32_t flags) { flag_broadcast_ = flags & IFF_BROADCAST; } -bool -IfaceMgr::send(const Pkt4Ptr& pkt) -{ - Iface* iface = getIface(pkt->getIface()); - if (!iface) { - isc_throw(BadValue, "Unable to send Pkt4. Invalid interface (" - << pkt->getIface() << ") specified."); - } - - memset(&control_buf_[0], 0, control_buf_len_); - - // Set the target address we're sending to. - sockaddr_in to; - memset(&to, 0, sizeof(to)); - to.sin_family = AF_INET; - to.sin_port = htons(pkt->getRemotePort()); - to.sin_addr.s_addr = htonl(pkt->getRemoteAddr()); - - struct msghdr m; - // Initialize our message header structure. - memset(&m, 0, sizeof(m)); - m.msg_name = &to; - m.msg_namelen = sizeof(to); - - // Set the data buffer we're sending. (Using this wacky - // "scatter-gather" stuff... we only have a single chunk - // of data to send, so we declare a single vector entry.) - struct iovec v; - memset(&v, 0, sizeof(v)); - // iov_base field is of void * type. We use it for packet - // transmission, so this buffer will not be modified. - v.iov_base = const_cast(pkt->getBuffer().getData()); - v.iov_len = pkt->getBuffer().getLength(); - m.msg_iov = &v; - m.msg_iovlen = 1; - - // call OS-specific routines (like setting interface index) - os_send4(m, control_buf_, control_buf_len_, pkt); - - pkt->updateTimestamp(); - - int result = sendmsg(getSocket(*pkt), &m, 0); - if (result < 0) { - isc_throw(SocketWriteError, "pkt4 send failed"); - } - return (result); -} +void IfaceMgr::os_send4(struct msghdr&, boost::scoped_array&, + size_t, const Pkt4Ptr&) { + return; -void IfaceMgr::os_send4(struct msghdr& m, boost::scoped_array& control_buf, - size_t control_buf_len, const Pkt4Ptr& pkt) { - - // Setting the interface is a bit more involved. - // - // We have to create a "control message", and set that to - // define the IPv4 packet information. We could set the - // source address if we wanted, but we can safely let the - // kernel decide what that should be. - m.msg_control = &control_buf[0]; - m.msg_controllen = control_buf_len; - struct cmsghdr* cmsg = CMSG_FIRSTHDR(&m); - cmsg->cmsg_level = IPPROTO_IP; - cmsg->cmsg_type = IP_PKTINFO; - cmsg->cmsg_len = CMSG_LEN(sizeof(struct in_pktinfo)); - struct in_pktinfo* pktinfo =(struct in_pktinfo *)CMSG_DATA(cmsg); - memset(pktinfo, 0, sizeof(struct in_pktinfo)); - pktinfo->ipi_ifindex = pkt->getIndex(); - m.msg_controllen = cmsg->cmsg_len; } -bool IfaceMgr::os_receive4(struct msghdr& m, Pkt4Ptr& pkt) { - struct cmsghdr* cmsg; - struct in_pktinfo* pktinfo; - struct in_addr to_addr; - - memset(&to_addr, 0, sizeof(to_addr)); - - cmsg = CMSG_FIRSTHDR(&m); - while (cmsg != NULL) { - if ((cmsg->cmsg_level == IPPROTO_IP) && - (cmsg->cmsg_type == IP_PKTINFO)) { - pktinfo = (struct in_pktinfo*)CMSG_DATA(cmsg); - - pkt->setIndex(pktinfo->ipi_ifindex); - pkt->setLocalAddr(IOAddress(htonl(pktinfo->ipi_addr.s_addr))); - return (true); - - // This field is useful, when we are bound to unicast - // address e.g. 192.0.2.1 and the packet was sent to - // broadcast. This will return broadcast address, not - // the address we are bound to. - - // XXX: Perhaps we should uncomment this: - // to_addr = pktinfo->ipi_spec_dst; - } - cmsg = CMSG_NXTHDR(&m, cmsg); - } - - return (false); +bool IfaceMgr::os_receive4(struct msghdr&, Pkt4Ptr&) { + return (true); } } // end of isc::dhcp namespace diff --git a/src/lib/dhcp/pkt_filter.h b/src/lib/dhcp/pkt_filter.h index 63a2e899bf..9edde666d4 100644 --- a/src/lib/dhcp/pkt_filter.h +++ b/src/lib/dhcp/pkt_filter.h @@ -20,6 +20,8 @@ namespace isc { namespace dhcp { +struct SocketInfo; + /// Forward declaration to the class representing interface class Iface; @@ -57,15 +59,20 @@ public: /// @brief Receive packet over specified socket. /// - /// @param sockfd descriptor of a socket to be used for packet reception - /// @param timeout_sec integral part of a timeout. - /// @param timeout_usec fractional part of a timeout (in microseconds). + /// @param iface interface + /// @param socket_info structure holding socket information /// /// @return Received packet - Pkt4Ptr receive(uint16_t sockfd, uint32_t timeout_sec, - uint32_t timeout_usec = 0); + virtual Pkt4Ptr receive(const Iface& iface, + const SocketInfo& socket_info) = 0; - // bool send(const Pkt4Ptr& pkt) = 0; + /// @brief Send packet over specified socket. + /// + /// @param sockfd socket descriptor + /// @param pkt packet to be sent + /// + /// @return result of sending the packet. It is 0 if successful. + virtual int send(uint16_t sockfd, const Pkt4Ptr& pkt) = 0; }; } // namespace isc::dhcp diff --git a/src/lib/dhcp/pkt_filter_inet.cc b/src/lib/dhcp/pkt_filter_inet.cc index ab343632cf..c863294d39 100644 --- a/src/lib/dhcp/pkt_filter_inet.cc +++ b/src/lib/dhcp/pkt_filter_inet.cc @@ -17,9 +17,17 @@ #include #include +using namespace isc::asiolink; + namespace isc { namespace dhcp { +PktFilterInet::PktFilterInet() + : control_buf_len_(CMSG_SPACE(sizeof(struct in6_pktinfo))), + control_buf_(new char[control_buf_len_]) +{ +} + int PktFilterInet::openSocket(const Iface& iface, const isc::asiolink::IOAddress& addr, @@ -77,10 +85,152 @@ PktFilterInet::openSocket(const Iface& iface, } #endif - return sock; + return (sock); } +Pkt4Ptr +PktFilterInet::receive(const Iface& iface, const SocketInfo& socket_info) { + struct sockaddr_in from_addr; + uint8_t buf[IfaceMgr::RCVBUFSIZE]; + + memset(&control_buf_[0], 0, control_buf_len_); + memset(&from_addr, 0, sizeof(from_addr)); + + // Initialize our message header structure. + struct msghdr m; + memset(&m, 0, sizeof(m)); + + // Point so we can get the from address. + m.msg_name = &from_addr; + m.msg_namelen = sizeof(from_addr); + + struct iovec v; + v.iov_base = static_cast(buf); + v.iov_len = IfaceMgr::RCVBUFSIZE; + m.msg_iov = &v; + m.msg_iovlen = 1; + + // Getting the interface is a bit more involved. + // + // We set up some space for a "control message". We have + // previously asked the kernel to give us packet + // information (when we initialized the interface), so we + // should get the destination address from that. + m.msg_control = &control_buf_[0]; + m.msg_controllen = control_buf_len_; + + int result = recvmsg(socket_info.sockfd_, &m, 0); + if (result < 0) { + isc_throw(SocketReadError, "failed to receive UDP4 data"); + } + + // We have all data let's create Pkt4 object. + Pkt4Ptr pkt = Pkt4Ptr(new Pkt4(buf, result)); + + pkt->updateTimestamp(); + + unsigned int ifindex = iface.getIndex(); + + IOAddress from(htonl(from_addr.sin_addr.s_addr)); + uint16_t from_port = htons(from_addr.sin_port); + + // Set receiving interface based on information, which socket was used to + // receive data. OS-specific info (see os_receive4()) may be more reliable, + // so this value may be overwritten. + pkt->setIndex(ifindex); + pkt->setIface(iface.getName()); + pkt->setRemoteAddr(from); + pkt->setRemotePort(from_port); + pkt->setLocalPort(socket_info.port_); + + struct cmsghdr* cmsg; + struct in_pktinfo* pktinfo; + struct in_addr to_addr; + + memset(&to_addr, 0, sizeof(to_addr)); + + cmsg = CMSG_FIRSTHDR(&m); + while (cmsg != NULL) { + if ((cmsg->cmsg_level == IPPROTO_IP) && + (cmsg->cmsg_type == IP_PKTINFO)) { + pktinfo = (struct in_pktinfo*)CMSG_DATA(cmsg); + + pkt->setIndex(pktinfo->ipi_ifindex); + pkt->setLocalAddr(IOAddress(htonl(pktinfo->ipi_addr.s_addr))); + break; + + // This field is useful, when we are bound to unicast + // address e.g. 192.0.2.1 and the packet was sent to + // broadcast. This will return broadcast address, not + // the address we are bound to. + + // XXX: Perhaps we should uncomment this: + // to_addr = pktinfo->ipi_spec_dst; + } + cmsg = CMSG_NXTHDR(&m, cmsg); + } + + return (pkt); +} + +int +PktFilterInet::send(uint16_t sockfd, const Pkt4Ptr& pkt) { + memset(&control_buf_[0], 0, control_buf_len_); + + // Set the target address we're sending to. + sockaddr_in to; + memset(&to, 0, sizeof(to)); + to.sin_family = AF_INET; + to.sin_port = htons(pkt->getRemotePort()); + to.sin_addr.s_addr = htonl(pkt->getRemoteAddr()); + + struct msghdr m; + // Initialize our message header structure. + memset(&m, 0, sizeof(m)); + m.msg_name = &to; + m.msg_namelen = sizeof(to); + + // Set the data buffer we're sending. (Using this wacky + // "scatter-gather" stuff... we only have a single chunk + // of data to send, so we declare a single vector entry.) + struct iovec v; + memset(&v, 0, sizeof(v)); + // iov_base field is of void * type. We use it for packet + // transmission, so this buffer will not be modified. + v.iov_base = const_cast(pkt->getBuffer().getData()); + v.iov_len = pkt->getBuffer().getLength(); + m.msg_iov = &v; + m.msg_iovlen = 1; + + // Setting the interface is a bit more involved. + // + // We have to create a "control message", and set that to + // define the IPv4 packet information. We could set the + // source address if we wanted, but we can safely let the + // kernel decide what that should be. + m.msg_control = &control_buf_[0]; + m.msg_controllen = control_buf_len_; + struct cmsghdr* cmsg = CMSG_FIRSTHDR(&m); + cmsg->cmsg_level = IPPROTO_IP; + cmsg->cmsg_type = IP_PKTINFO; + cmsg->cmsg_len = CMSG_LEN(sizeof(struct in_pktinfo)); + struct in_pktinfo* pktinfo =(struct in_pktinfo *)CMSG_DATA(cmsg); + memset(pktinfo, 0, sizeof(struct in_pktinfo)); + pktinfo->ipi_ifindex = pkt->getIndex(); + m.msg_controllen = cmsg->cmsg_len; + + pkt->updateTimestamp(); + + int result = sendmsg(sockfd, &m, 0); + if (result < 0) { + isc_throw(SocketWriteError, "pkt4 send failed"); + } + + return (result); +} + + } // end of isc::dhcp namespace } // end of isc namespace diff --git a/src/lib/dhcp/pkt_filter_inet.h b/src/lib/dhcp/pkt_filter_inet.h index 3bc1545733..4e98612a21 100644 --- a/src/lib/dhcp/pkt_filter_inet.h +++ b/src/lib/dhcp/pkt_filter_inet.h @@ -27,6 +27,11 @@ namespace dhcp { class PktFilterInet : public PktFilter { public: + /// @brief Constructor + /// + /// Allocates control buffer. + PktFilterInet(); + /// @brief Open socket. /// /// @param iface interface descriptor @@ -44,15 +49,25 @@ public: /// @brief Receive packet over specified socket. /// - /// @param sockfd descriptor of a socket to be used for packet reception - /// @param timeout_sec integral part of a timeout. - /// @param timeout_usec fractional part of a timeout (in microseconds). + /// @param iface interface + /// @param socket_info structure holding socket information /// /// @return Received packet - Pkt4Ptr receive(uint16_t sockfd, uint32_t timeout_sec, - uint32_t timeout_usec = 0); + virtual Pkt4Ptr receive(const Iface& iface, const SocketInfo& socket_info); + + /// @brief Send packet over specified socket. + /// + /// @param sockfd socket descriptor + /// @param pkt packet to be sent + /// + /// @return result of sending a packet. It is 0 if successful. + virtual int send(uint16_t sockfd, const Pkt4Ptr& pkt); - // bool send(const Pkt4Ptr& pkt) = 0; +private: + /// Length of the control_buf_ array. + size_t control_buf_len_; + /// Control buffer, used in transmission and reception. + boost::scoped_array control_buf_; }; } // namespace isc::dhcp diff --git a/src/lib/dhcp/tests/iface_mgr_unittest.cc b/src/lib/dhcp/tests/iface_mgr_unittest.cc index 8bb303ffae..f4ce398a9d 100644 --- a/src/lib/dhcp/tests/iface_mgr_unittest.cc +++ b/src/lib/dhcp/tests/iface_mgr_unittest.cc @@ -863,14 +863,14 @@ TEST_F(IfaceMgrTest, iface_methods) { TEST_F(IfaceMgrTest, socketInfo) { // check that socketinfo for IPv4 socket is functional - Iface::SocketInfo sock1(7, IOAddress("192.0.2.56"), DHCP4_SERVER_PORT + 7); + SocketInfo sock1(7, IOAddress("192.0.2.56"), DHCP4_SERVER_PORT + 7); EXPECT_EQ(7, sock1.sockfd_); EXPECT_EQ("192.0.2.56", sock1.addr_.toText()); EXPECT_EQ(AF_INET, sock1.family_); EXPECT_EQ(DHCP4_SERVER_PORT + 7, sock1.port_); // check that socketinfo for IPv6 socket is functional - Iface::SocketInfo sock2(9, IOAddress("2001:db8:1::56"), DHCP4_SERVER_PORT + 9); + SocketInfo sock2(9, IOAddress("2001:db8:1::56"), DHCP4_SERVER_PORT + 9); EXPECT_EQ(9, sock2.sockfd_); EXPECT_EQ("2001:db8:1::56", sock2.addr_.toText()); EXPECT_EQ(AF_INET6, sock2.family_);