Kea 1.5.0
pkt_filter_inet.cc
Go to the documentation of this file.
1// Copyright (C) 2013-2018 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#include <dhcp/iface_mgr.h>
9#include <dhcp/pkt4.h>
11#include <errno.h>
12#include <cstring>
13#include <fcntl.h>
14
15using namespace isc::asiolink;
16
17namespace isc {
18namespace dhcp {
19
21 : recv_control_buf_len_(CMSG_SPACE(sizeof(struct in6_pktinfo))),
22 send_control_buf_len_(CMSG_SPACE(sizeof(struct in6_pktinfo))),
23 recv_control_buf_(new char[recv_control_buf_len_]),
24 send_control_buf_(new char[send_control_buf_len_])
25{
26}
27
30 const isc::asiolink::IOAddress& addr,
31 const uint16_t port,
32 const bool receive_bcast,
33 const bool send_bcast) {
34
35 struct sockaddr_in addr4;
36 memset(&addr4, 0, sizeof(sockaddr));
37 addr4.sin_family = AF_INET;
38 addr4.sin_port = htons(port);
39
40 // If we are to receive broadcast messages we have to bind
41 // to "ANY" address.
42 if (receive_bcast && iface.flag_broadcast_) {
43 addr4.sin_addr.s_addr = INADDR_ANY;
44 } else {
45 addr4.sin_addr.s_addr = htonl(addr.toUint32());
46 }
47
48 int sock = socket(AF_INET, SOCK_DGRAM, 0);
49 if (sock < 0) {
50 isc_throw(SocketConfigError, "Failed to create UDP4 socket.");
51 }
52
53 // Set the close-on-exec flag.
54 if (fcntl(sock, F_SETFD, FD_CLOEXEC) < 0) {
55 close(sock);
56 isc_throw(SocketConfigError, "Failed to set close-on-exec flag"
57 << " on socket " << sock);
58 }
59
60#ifdef SO_BINDTODEVICE
61 if (receive_bcast && iface.flag_broadcast_) {
62 // Bind to device so as we receive traffic on a specific interface.
63 if (setsockopt(sock, SOL_SOCKET, SO_BINDTODEVICE, iface.getName().c_str(),
64 iface.getName().length() + 1) < 0) {
65 close(sock);
66 isc_throw(SocketConfigError, "Failed to set SO_BINDTODEVICE option"
67 << " on socket " << sock);
68 }
69 }
70#endif
71
72 if (send_bcast && iface.flag_broadcast_) {
73 // Enable sending to broadcast address.
74 int flag = 1;
75 if (setsockopt(sock, SOL_SOCKET, SO_BROADCAST, &flag, sizeof(flag)) < 0) {
76 close(sock);
77 isc_throw(SocketConfigError, "Failed to set SO_BROADCAST option"
78 << " on socket " << sock);
79 }
80 }
81
82 if (bind(sock, (struct sockaddr *)&addr4, sizeof(addr4)) < 0) {
83 close(sock);
84 isc_throw(SocketConfigError, "Failed to bind socket " << sock
85 << " to " << addr
86 << "/port=" << port);
87 }
88
89 // On Linux systems IP_PKTINFO socket option is supported. This
90 // option is used to retrieve destination address of the packet.
91#if defined (IP_PKTINFO) && defined (OS_LINUX)
92 int flag = 1;
93 if (setsockopt(sock, IPPROTO_IP, IP_PKTINFO, &flag, sizeof(flag)) != 0) {
94 close(sock);
95 isc_throw(SocketConfigError, "setsockopt: IP_PKTINFO: failed.");
96 }
97
98 // On BSD systems IP_RECVDSTADDR is used instead of IP_PKTINFO.
99#elif defined (IP_RECVDSTADDR) && defined (OS_BSD)
100 int flag = 1;
101 if (setsockopt(sock, IPPROTO_IP, IP_RECVDSTADDR, &flag, sizeof(flag)) != 0) {
102 close(sock);
103 isc_throw(SocketConfigError, "setsockopt: IP_RECVDSTADDR: failed.");
104 }
105#endif
106
107 SocketInfo sock_desc(addr, port, sock);
108 return (sock_desc);
109
110}
111
113PktFilterInet::receive(Iface& iface, const SocketInfo& socket_info) {
114 struct sockaddr_in from_addr;
115 uint8_t buf[IfaceMgr::RCVBUFSIZE];
116
117 memset(&recv_control_buf_[0], 0, recv_control_buf_len_);
118 memset(&from_addr, 0, sizeof(from_addr));
119
120 // Initialize our message header structure.
121 struct msghdr m;
122 memset(&m, 0, sizeof(m));
123
124 // Point so we can get the from address.
125 m.msg_name = &from_addr;
126 m.msg_namelen = sizeof(from_addr);
127
128 struct iovec v;
129 v.iov_base = static_cast<void*>(buf);
130 v.iov_len = IfaceMgr::RCVBUFSIZE;
131 m.msg_iov = &v;
132 m.msg_iovlen = 1;
133
134 // Getting the interface is a bit more involved.
135 //
136 // We set up some space for a "control message". We have
137 // previously asked the kernel to give us packet
138 // information (when we initialized the interface), so we
139 // should get the destination address from that.
140 m.msg_control = &recv_control_buf_[0];
141 m.msg_controllen = recv_control_buf_len_;
142
143 int result = recvmsg(socket_info.sockfd_, &m, 0);
144 if (result < 0) {
145 isc_throw(SocketReadError, "failed to receive UDP4 data");
146 }
147
148 // We have all data let's create Pkt4 object.
149 Pkt4Ptr pkt = Pkt4Ptr(new Pkt4(buf, result));
150
151 pkt->updateTimestamp();
152
153 unsigned int ifindex = iface.getIndex();
154
155 IOAddress from(htonl(from_addr.sin_addr.s_addr));
156 uint16_t from_port = htons(from_addr.sin_port);
157
158 // Set receiving interface based on information, which socket was used to
159 // receive data. OS-specific info (see os_receive4()) may be more reliable,
160 // so this value may be overwritten.
161 pkt->setIndex(ifindex);
162 pkt->setIface(iface.getName());
163 pkt->setRemoteAddr(from);
164 pkt->setRemotePort(from_port);
165 pkt->setLocalPort(socket_info.port_);
166
167// Linux systems support IP_PKTINFO option which is used to retrieve the
168// destination address of the received packet. On BSD systems IP_RECVDSTADDR
169// is used instead.
170#if defined (IP_PKTINFO) && defined (OS_LINUX)
171 struct in_pktinfo* pktinfo;
172 struct cmsghdr* cmsg = CMSG_FIRSTHDR(&m);
173
174 while (cmsg != NULL) {
175 if ((cmsg->cmsg_level == IPPROTO_IP) &&
176 (cmsg->cmsg_type == IP_PKTINFO)) {
177 pktinfo = reinterpret_cast<struct in_pktinfo*>(CMSG_DATA(cmsg));
178
179 pkt->setIndex(pktinfo->ipi_ifindex);
180 pkt->setLocalAddr(IOAddress(htonl(pktinfo->ipi_addr.s_addr)));
181 break;
182
183 // This field is useful, when we are bound to unicast
184 // address e.g. 192.0.2.1 and the packet was sent to
185 // broadcast. This will return broadcast address, not
186 // the address we are bound to.
187
188 // XXX: Perhaps we should uncomment this:
189 // to_addr = pktinfo->ipi_spec_dst;
190 }
191 cmsg = CMSG_NXTHDR(&m, cmsg);
192 }
193
194#elif defined (IP_RECVDSTADDR) && defined (OS_BSD)
195 struct in_addr* to_addr;
196 struct cmsghdr* cmsg = CMSG_FIRSTHDR(&m);
197
198 while (cmsg != NULL) {
199 if ((cmsg->cmsg_level == IPPROTO_IP) &&
200 (cmsg->cmsg_type == IP_RECVDSTADDR)) {
201 to_addr = reinterpret_cast<struct in_addr*>(CMSG_DATA(cmsg));
202 pkt->setLocalAddr(IOAddress(htonl(to_addr->s_addr)));
203 break;
204 }
205 cmsg = CMSG_NXTHDR(&m, cmsg);
206 }
207
208#endif
209
210 return (pkt);
211}
212
213int
214PktFilterInet::send(const Iface&, uint16_t sockfd,
215 const Pkt4Ptr& pkt) {
216 memset(&send_control_buf_[0], 0, send_control_buf_len_);
217
218 // Set the target address we're sending to.
219 sockaddr_in to;
220 memset(&to, 0, sizeof(to));
221 to.sin_family = AF_INET;
222 to.sin_port = htons(pkt->getRemotePort());
223 to.sin_addr.s_addr = htonl(pkt->getRemoteAddr().toUint32());
224
225 struct msghdr m;
226 // Initialize our message header structure.
227 memset(&m, 0, sizeof(m));
228 m.msg_name = &to;
229 m.msg_namelen = sizeof(to);
230
231 // Set the data buffer we're sending. (Using this wacky
232 // "scatter-gather" stuff... we only have a single chunk
233 // of data to send, so we declare a single vector entry.)
234 struct iovec v;
235 memset(&v, 0, sizeof(v));
236 // iov_base field is of void * type. We use it for packet
237 // transmission, so this buffer will not be modified.
238 v.iov_base = const_cast<void *>(pkt->getBuffer().getData());
239 v.iov_len = pkt->getBuffer().getLength();
240 m.msg_iov = &v;
241 m.msg_iovlen = 1;
242
243// In the future the OS-specific code may be abstracted to a different
244// file but for now we keep it here because there is no code yet, which
245// is specific to non-Linux systems.
246#if defined (IP_PKTINFO) && defined (OS_LINUX)
247 // Setting the interface is a bit more involved.
248 //
249 // We have to create a "control message", and set that to
250 // define the IPv4 packet information. We set the source address
251 // to handle correctly interfaces with multiple addresses.
252 m.msg_control = &send_control_buf_[0];
253 m.msg_controllen = send_control_buf_len_;
254 struct cmsghdr* cmsg = CMSG_FIRSTHDR(&m);
255 cmsg->cmsg_level = IPPROTO_IP;
256 cmsg->cmsg_type = IP_PKTINFO;
257 cmsg->cmsg_len = CMSG_LEN(sizeof(struct in_pktinfo));
258 struct in_pktinfo* pktinfo =(struct in_pktinfo *)CMSG_DATA(cmsg);
259 memset(pktinfo, 0, sizeof(struct in_pktinfo));
260
261 // In some cases the index of the outbound interface is not set. This
262 // is a matter of configuration. When the server is configured to
263 // determine the outbound interface based on routing information,
264 // the index is left unset (negative).
265 if (pkt->indexSet()) {
266 pktinfo->ipi_ifindex = pkt->getIndex();
267 }
268
269 // When the DHCP server is using routing to determine the outbound
270 // interface, the local address is also left unset.
271 if (!pkt->getLocalAddr().isV4Zero()) {
272 pktinfo->ipi_spec_dst.s_addr = htonl(pkt->getLocalAddr().toUint32());
273 }
274
275 m.msg_controllen = CMSG_SPACE(sizeof(struct in_pktinfo));
276#endif
277
278 pkt->updateTimestamp();
279
280 int result = sendmsg(sockfd, &m, 0);
281 if (result < 0) {
282 isc_throw(SocketWriteError, "pkt4 send failed: sendmsg() returned "
283 " with an error: " << strerror(errno));
284 }
285
286 return (result);
287}
288
289
290
291} // end of isc::dhcp namespace
292} // end of isc namespace
static const uint32_t RCVBUFSIZE
Packet reception buffer size.
Definition: iface_mgr.h:502
Represents a single network interface.
Definition: iface_mgr.h:114
std::string getName() const
Returns interface name.
Definition: iface_mgr.h:216
bool flag_broadcast_
Flag specifies if selected interface is broadcast capable.
Definition: iface_mgr.h:433
uint16_t getIndex() const
Returns interface index.
Definition: iface_mgr.h:211
Represents DHCPv4 packet.
Definition: pkt4.h:38
virtual SocketInfo openSocket(Iface &iface, const isc::asiolink::IOAddress &addr, const uint16_t port, const bool receive_bcast, const bool send_bcast)
Open primary and fallback socket.
virtual int send(const Iface &iface, uint16_t sockfd, const Pkt4Ptr &pkt)
Send packet over specified socket.
virtual Pkt4Ptr receive(Iface &iface, const SocketInfo &socket_info)
Receive packet over specified socket.
IfaceMgr exception thrown thrown when socket opening or configuration failed.
Definition: iface_mgr.h:59
IfaceMgr exception thrown thrown when error occurred during reading data from socket.
Definition: iface_mgr.h:67
IfaceMgr exception thrown thrown when error occurred during sending data through socket.
Definition: iface_mgr.h:75
#define isc_throw(type, stream)
A shortcut macro to insert known values into exception arguments.
boost::shared_ptr< Pkt4 > Pkt4Ptr
A pointer to Pkt4 object.
Definition: pkt4.h:546
Defines the logger used by the top-level component of kea-dhcp-ddns.
Holds information about socket.
Definition: socket_info.h:19
int sockfd_
IPv4 or IPv6.
Definition: socket_info.h:26
uint16_t port_
bound address
Definition: socket_info.h:22