1 // Copyright (C) 2013-2015 Internet Systems Consortium, Inc. ("ISC")
2 //
3 // This Source Code Form is subject to the terms of the Mozilla Public
4 // License, v. 2.0. If a copy of the MPL was not distributed with this
5 // file, You can obtain one at http://mozilla.org/MPL/2.0/.
6 
7 #include <config.h>
8 
9 #include <asiolink/io_address.h>
10 #include <dhcp/pkt6.h>
11 #include <dhcp/tests/pkt_filter6_test_utils.h>
12 
13 #include <boost/foreach.hpp>
14 
15 #include <arpa/inet.h>
16 #include <netinet/in.h>
17 #include <sys/socket.h>
18 
19 using namespace isc::asiolink;
20 
21 namespace isc {
22 namespace dhcp {
23 namespace test {
24 
PktFilter6Test(const uint16_t port)25 PktFilter6Test::PktFilter6Test(const uint16_t port)
26     : port_(port),
27       sock_info_(isc::asiolink::IOAddress("::1"), port, -1, -1),
28       send_msg_sock_(-1) {
29     // Initialize ifname_ and ifindex_.
30     loInit();
31     // Initialize test_message_.
32     initTestMessage();
33 }
34 
~PktFilter6Test()35 PktFilter6Test::~PktFilter6Test() {
36     // Cleanup after each test. This guarantees
37     // that the sockets do not hang after a test.
38     if (sock_info_.sockfd_ >= 0) {
39         close(sock_info_.sockfd_);
40     }
41     if (sock_info_.fallbackfd_ >=0) {
42         close(sock_info_.fallbackfd_);
43     }
44     if (send_msg_sock_ >= 0) {
45         close(send_msg_sock_);
46     }
47 }
48 
49 void
initTestMessage()50 PktFilter6Test::initTestMessage() {
51     // Let's create a DHCPv6 message instance.
52     test_message_.reset(new Pkt6(DHCPV6_ADVERTISE, 123));
53 
54     // Set required fields.
55     test_message_->setLocalAddr(IOAddress("::1"));
56     test_message_->setRemoteAddr(IOAddress("::1"));
57     test_message_->setRemotePort(port_);
58     test_message_->setLocalPort(port_ + 1);
59     test_message_->setIndex(ifindex_);
60     test_message_->setIface(ifname_);
61 
62     try {
63         test_message_->pack();
64     } catch (const isc::Exception& ex) {
65         ADD_FAILURE() << "failed to create test message for PktFilter6Test";
66     }
67 }
68 
69 void
loInit()70 PktFilter6Test::loInit() {
71     if (if_nametoindex("lo") > 0) {
72         ifname_ = "lo";
73         ifindex_ = if_nametoindex("lo");
74 
75     } else if (if_nametoindex("lo0") > 0) {
76         ifname_ = "lo0";
77         ifindex_ = if_nametoindex("lo0");
78 
79     } else {
80         std::cout << "Failed to detect loopback interface. Neither "
81                   << "lo nor lo0 worked. Giving up." << std::endl;
82         FAIL();
83 
84     }
85 }
86 
87 void
sendMessage()88 PktFilter6Test::sendMessage() {
89     // DHCPv6 message will be sent over loopback interface.
90     Iface iface(ifname_, ifindex_);
91     IOAddress addr("::1");
92 
93     // Initialize the source address and port.
94     struct sockaddr_in6 addr6;
95     memset(&addr6, 0, sizeof(addr6));
96     addr6.sin6_family = AF_INET6;
97     addr6.sin6_port = htons(port_);
98     memcpy(&addr6.sin6_addr, &addr.toBytes()[0], sizeof(addr6.sin6_addr));
99 
100     // Open socket and bind to source address and port.
101     send_msg_sock_ = socket(AF_INET6, SOCK_DGRAM, 0);
102     ASSERT_GE(send_msg_sock_, 0);
103 
104     ASSERT_GE(bind(send_msg_sock_, (struct sockaddr *)&addr6,
105                    sizeof(addr6)), 0);
106 
107     // Set the destination address and port.
108     struct sockaddr_in6 dest_addr6;
109     memset(&dest_addr6, 0, sizeof(sockaddr_in6));
110     dest_addr6.sin6_family = AF_INET6;
111     dest_addr6.sin6_port = htons(port_ + 1);
112     memcpy(&dest_addr6.sin6_addr, &addr.toBytes()[0], 16);
113 
114     // Initialize the message header structure, required by sendmsg.
115     struct msghdr m;
116     memset(&m, 0, sizeof(m));
117     m.msg_name = &dest_addr6;
118     m.msg_namelen = sizeof(dest_addr6);
119     // The iovec structure holds the packet data.
120     struct iovec v;
121     memset(&v, 0, sizeof(v));
122     v.iov_base = const_cast<void *>(test_message_->getBuffer().getData());
123     v.iov_len = test_message_->getBuffer().getLength();
124     // Assign the iovec to msghdr structure.
125     m.msg_iov = &v;
126     m.msg_iovlen = 1;
127     // We should be able to send the whole message. The sendmsg function should
128     // return the number of bytes sent, which is equal to the size of our
129     // message.
130     ASSERT_EQ(sendmsg(send_msg_sock_, &m, 0),
131               test_message_->getBuffer().getLength());
132     close(send_msg_sock_);
133     send_msg_sock_ = -1;
134 
135 }
136 
137 void
testDgramSocket(const int sock) const138 PktFilter6Test::testDgramSocket(const int sock) const {
139     // Check that socket has been opened.
140     ASSERT_GE(sock, 0);
141 
142     // Verify that the socket belongs to AF_INET family.
143     sockaddr_in6 sock_address;
144     socklen_t sock_address_len = sizeof(sock_address);
145     ASSERT_EQ(0, getsockname(sock,
146                              reinterpret_cast<sockaddr*>(&sock_address),
147                              &sock_address_len));
148     EXPECT_EQ(AF_INET6, sock_address.sin6_family);
149 
150     // Verify that the socket is bound the appropriate address.
151     char straddr[INET6_ADDRSTRLEN];
152     inet_ntop(AF_INET6, &sock_address.sin6_addr, straddr, sizeof(straddr));
153     std::string bind_addr(straddr);
154     EXPECT_EQ("::1", bind_addr);
155 
156     // Verify that the socket is bound to appropriate port.
157     EXPECT_EQ(port_, ntohs(sock_address.sin6_port));
158 
159     // Verify that the socket has SOCK_DGRAM type.
160     int sock_type;
161     socklen_t sock_type_len = sizeof(sock_type);
162     ASSERT_EQ(0, getsockopt(sock, SOL_SOCKET, SO_TYPE,
163                             &sock_type, &sock_type_len));
164     EXPECT_EQ(SOCK_DGRAM, sock_type);
165 }
166 
167 void
testRcvdMessage(const Pkt6Ptr & rcvd_msg) const168 PktFilter6Test::testRcvdMessage(const Pkt6Ptr& rcvd_msg) const {
169     // Currently, we don't send any payload in the message.
170     // Let's just check that the transaction id matches so as we
171     // are sure that we received the message that we expected.
172     EXPECT_EQ(test_message_->getTransid(), rcvd_msg->getTransid());
173 }
174 
PktFilter6Stub()175 PktFilter6Stub::PktFilter6Stub()
176     : open_socket_count_ (0) {
177 }
178 
179 SocketInfo
openSocket(const Iface & iface,const isc::asiolink::IOAddress & addr,const uint16_t port,const bool)180 PktFilter6Stub::openSocket(const Iface& iface, const isc::asiolink::IOAddress& addr,
181                            const uint16_t port, const bool) {
182     // Check if there is any other socket bound to the specified address
183     // and port on this interface.
184     BOOST_FOREACH(SocketInfo socket, iface.getSockets()) {
185         if ((socket.addr_ == addr) && (socket.port_ == port)) {
186             isc_throw(SocketConfigError, "test socket bind error");
187         }
188     }
189     ++open_socket_count_;
190     return (SocketInfo(addr, port, 0));
191 }
192 
193 Pkt6Ptr
receive(const SocketInfo &)194 PktFilter6Stub::receive(const SocketInfo&) {
195     return Pkt6Ptr();
196 }
197 
198 int
send(const Iface &,uint16_t,const Pkt6Ptr &)199 PktFilter6Stub::send(const Iface&, uint16_t, const Pkt6Ptr&) {
200     return (0);
201 }
202 
203 
204 } // end of isc::dhcp::test namespace
205 } // end of isc::dhcp namespace
206 } // end of isc namespace
207