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