Kea  2.1.7-git
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>
10 #include <dhcp/pkt_filter_inet.h>
11 #include <errno.h>
12 #include <cstring>
13 #include <fcntl.h>
14 
15 using namespace isc::asiolink;
16 
17 namespace isc {
18 namespace dhcp {
19 
20 const size_t
21 PktFilterInet::CONTROL_BUF_LEN = CMSG_SPACE(sizeof(struct in6_pktinfo));
22 
23 SocketInfo
24 PktFilterInet::openSocket(Iface& iface,
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 
106 Pkt4Ptr
107 PktFilterInet::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 
208 int
209 PktFilterInet::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
IfaceMgr exception thrown thrown when socket opening or configuration failed.
Definition: iface_mgr.h:63
int getIndex() const
Returns interface index.
Definition: iface_mgr.h:216
Represents a single network interface.
Definition: iface_mgr.h:118
int sockfd_
IPv4 or IPv6.
Definition: socket_info.h:26
#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:544
uint16_t port_
bound address
Definition: socket_info.h:22
std::string getName() const
Returns interface name.
Definition: iface_mgr.h:221
IfaceMgr exception thrown thrown when error occurred during reading data from socket.
Definition: iface_mgr.h:71
bool flag_broadcast_
Flag specifies if selected interface is broadcast capable.
Definition: iface_mgr.h:438
Defines the logger used by the top-level component of kea-lfc.
Represents DHCPv4 packet.
Definition: pkt4.h:37
IfaceMgr exception thrown thrown when error occurred during sending data through socket.
Definition: iface_mgr.h:79
Holds information about socket.
Definition: socket_info.h:19