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>
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 PktFilterInet::PktFilterInet()
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 
112 Pkt4Ptr
113 PktFilterInet::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 
213 int
214 PktFilterInet::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
IfaceMgr exception thrown thrown when socket opening or configuration failed.
Definition: iface_mgr.h:59
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.
Represents a single network interface.
Definition: iface_mgr.h:114
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.
virtual int send(const Iface &iface, uint16_t sockfd, const Pkt4Ptr &pkt)
Send packet over specified socket.
boost::shared_ptr< Pkt4 > Pkt4Ptr
A pointer to Pkt4 object.
Definition: pkt4.h:546
uint16_t port_
bound address
Definition: socket_info.h:22
std::string getName() const
Returns interface name.
Definition: iface_mgr.h:216
IfaceMgr exception thrown thrown when error occurred during reading data from socket.
Definition: iface_mgr.h:67
bool flag_broadcast_
Flag specifies if selected interface is broadcast capable.
Definition: iface_mgr.h:433
static const uint32_t RCVBUFSIZE
Packet reception buffer size.
Definition: iface_mgr.h:502
Defines the logger used by the top-level component of kea-dhcp-ddns.
Represents DHCPv4 packet.
Definition: pkt4.h:38
uint16_t getIndex() const
Returns interface index.
Definition: iface_mgr.h:211
virtual Pkt4Ptr receive(Iface &iface, const SocketInfo &socket_info)
Receive packet over specified socket.
IfaceMgr exception thrown thrown when error occurred during sending data through socket.
Definition: iface_mgr.h:75
Holds information about socket.
Definition: socket_info.h:19