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