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