|
@@ -17,9 +17,17 @@
|
|
|
#include <dhcp/pkt4.h>
|
|
|
#include <dhcp/pkt_filter_inet.h>
|
|
|
|
|
|
+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<void*>(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<void *>(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
|