Kea 2.7.1
dhcp4o6_ipc.cc
Go to the documentation of this file.
1// Copyright (C) 2015-2024 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
9#include <dhcp/dhcp6.h>
10#include <dhcp/iface_mgr.h>
12#include <dhcp/option_custom.h>
13#include <dhcp/option_int.h>
14#include <dhcp/option_string.h>
15#include <dhcp/option_vendor.h>
16#include <dhcpsrv/dhcp4o6_ipc.h>
17#include <dhcpsrv/dhcpsrv_log.h>
18
19#include <boost/pointer_cast.hpp>
20
21#include <errno.h>
22#include <netinet/in.h>
23#include <fcntl.h>
24#include <string>
25
26using namespace isc::asiolink;
27using namespace isc::util;
28using namespace std;
29
30namespace isc {
31namespace dhcp {
32
33Dhcp4o6IpcBase::Dhcp4o6IpcBase() : port_(0), socket_fd_(-1) {}
34
38
39int Dhcp4o6IpcBase::open(uint16_t port, EndpointType endpoint_type) {
40 // Don't check if the value is greater than 65534 as it is done
41 // by callers before they cast the value to 16 bits.
42
43 if (port == port_) {
44 // No change: nothing to do
45 return (socket_fd_);
46 }
47
48 // Open socket
49 int sock = socket(PF_INET6, SOCK_DGRAM, IPPROTO_UDP);
50 if (sock < 0) {
51 isc_throw(Dhcp4o6IpcError, "Failed to create DHCP4o6 socket.");
52 }
53
54 // Set no blocking
55 if (fcntl(sock, F_SETFL, O_NONBLOCK) < 0) {
56 ::close(sock);
58 "Failed to set O_NONBLOCK on DHCP4o6 socket.");
59 }
60
61 // Bind to the local address
62 struct sockaddr_in6 local6;
63 memset(&local6, 0, sizeof(local6));
64 local6.sin6_family = AF_INET6;
65#ifdef HAVE_SA_LEN
66 local6.sin6_len = sizeof(local6);
67#endif
68 if (endpoint_type == ENDPOINT_TYPE_V6) {
69 local6.sin6_port = htons(port);
70 } else {
71 local6.sin6_port = htons(port + 1);
72 }
73 // We'll connect to the loopback address so bind to it too.
74 local6.sin6_addr.s6_addr[15] = 1;
75 if (::bind(sock, (struct sockaddr *)&local6, sizeof(local6)) < 0) {
76 ::close(sock);
77 isc_throw(Dhcp4o6IpcError, "Failed to bind DHCP4o6 socket.");
78 }
79
80 // Connect to the remote address
81 struct sockaddr_in6 remote6;
82 memset(&remote6, 0, sizeof(remote6));
83 remote6.sin6_family = AF_INET6;
84#ifdef HAVE_SA_LEN
85 remote6.sin6_len = sizeof(remote6);
86#endif
87 if (endpoint_type == ENDPOINT_TYPE_V6) {
88 remote6.sin6_port = htons(port + 1);
89 } else {
90 remote6.sin6_port = htons(port);
91 }
92 // At least OpenBSD requires the remote address to not be left
93 // unspecified, so we set it to the loopback address.
94 remote6.sin6_addr.s6_addr[15] = 1;
95 if (connect(sock, reinterpret_cast<const struct sockaddr*>(&remote6),
96 sizeof(remote6)) < 0) {
97 ::close(sock);
98 isc_throw(Dhcp4o6IpcError, "Failed to connect DHCP4o6 socket.");
99 }
100
101 if (socket_fd_ != -1) {
102 if (dup2(sock, socket_fd_) == -1) {
103 ::close(sock);
104 isc_throw(Dhcp4o6IpcError, "Failed to duplicate DHCP4o6 socket.");
105 }
106 if (sock != socket_fd_) {
107 ::close(sock);
108 sock = socket_fd_;
109 }
110 }
111
112 // Success
113 port_ = port;
114 socket_fd_ = sock;
115 return (socket_fd_);
116}
117
119 port_ = 0;
120 if (socket_fd_ != -1) {
121 IfaceMgr::instance().deleteExternalSocket(socket_fd_);
123 socket_fd_ = -1;
124 }
125}
126
128 uint8_t buf[65536];
129 ssize_t cc = recv(socket_fd_, buf, sizeof(buf), 0);
130 if (cc < 0) {
131 isc_throw(Dhcp4o6IpcError, "Failed to receive on DHCP4o6 socket.");
132 }
133 Pkt6Ptr pkt = Pkt6Ptr(new Pkt6(buf, cc));
134 pkt->updateTimestamp();
135
136 // Get interface name and remote address
137 pkt->unpack();
138
139 // Vendor option is initially NULL. If we find the instance of the vendor
140 // option with the ISC enterprise id this pointer will point to it.
141 OptionVendorPtr option_vendor;
142
143 // Get all vendor option and look for the one with the ISC enterprise id.
144 OptionCollection vendor_options = pkt->getOptions(D6O_VENDOR_OPTS);
145 for (auto const& opt : vendor_options) {
146 option_vendor = boost::dynamic_pointer_cast<OptionVendor>(opt.second);
147 if (option_vendor) {
148 if (option_vendor->getVendorId() == ENTERPRISE_ID_ISC) {
149 break;
150 }
151 option_vendor.reset();
152 }
153 }
154
155 // Vendor option must exist.
156 if (!option_vendor) {
158 .arg("no ISC vendor option");
159 isc_throw(Dhcp4o6IpcError, "malformed packet (no ISC vendor option)");
160 }
161
162 // The option carrying interface name is required.
163 OptionStringPtr ifname = boost::dynamic_pointer_cast<
164 OptionString>(option_vendor->getOption(ISC_V6_4O6_INTERFACE));
165 if (!ifname) {
167 .arg("no interface suboption");
169 "malformed packet (interface suboption missing "
170 "or has incorrect type)");
171 }
172
173 // Check if this interface is present in the system.
174 IfacePtr iface = IfaceMgr::instance().getIface(ifname->getValue());
175 if (!iface) {
177 .arg("can't get interface " + ifname->getValue());
179 "malformed packet (unknown interface "
180 + ifname->getValue() + ")");
181 }
182
183 // Get the option holding source IPv6 address.
184 OptionCustomPtr srcs = boost::dynamic_pointer_cast<
185 OptionCustom>(option_vendor->getOption(ISC_V6_4O6_SRC_ADDRESS));
186 if (!srcs) {
188 .arg("no source address suboption");
190 "malformed packet (source address suboption missing "
191 "or has incorrect type)");
192 }
193
194 // Get the option holding source port.
195 OptionUint16Ptr sport = boost::dynamic_pointer_cast<
196 OptionUint16>(option_vendor->getOption(ISC_V6_4O6_SRC_PORT));
197 if (!sport) {
199 .arg("no source port suboption");
201 "malformed packet (source port suboption missing "
202 "or has incorrect type)");
203 }
204
205 // Update the packet.
206 pkt->setRemoteAddr(srcs->readAddress());
207 pkt->setRemotePort(sport->getValue());
208 pkt->setIface(iface->getName());
209 pkt->setIndex(iface->getIndex());
210
211 // Remove options that have been added by the IPC sender.
212 static_cast<void>(option_vendor->delOption(ISC_V6_4O6_INTERFACE));
213 static_cast<void>(option_vendor->delOption(ISC_V6_4O6_SRC_ADDRESS));
214 static_cast<void>(option_vendor->delOption(ISC_V6_4O6_SRC_PORT));
215
216 // If there are no more options, the IPC sender has probably created the
217 // vendor option, in which case we should remove it here.
218 if (option_vendor->getOptions().empty()) {
219 static_cast<void>(pkt->delOption(D6O_VENDOR_OPTS));
220 }
221
222 return (pkt);
223}
224
226 // This shouldn't happen, i.e. send() shouldn't be called if there is
227 // no message.
228 if (!pkt) {
229 isc_throw(Dhcp4o6IpcError, "DHCP4o6 message must not be NULL while"
230 " trying to send it over the IPC");
231 }
232
233 // Disabled: nowhere to send
234 if (socket_fd_ == -1) {
235 isc_throw(Dhcp4o6IpcError, "unable to send DHCP4o6 message because"
236 " IPC socket is closed");
237 }
238
239 // Check if vendor option exists.
240 // Vendor option is initially NULL. If we find the instance of the vendor
241 // option with the ISC enterprise id this pointer will point to it.
242 OptionVendorPtr option_vendor;
243
244 // Get all vendor option and look for the one with the ISC enterprise id.
245 OptionCollection vendor_options = pkt->getOptions(D6O_VENDOR_OPTS);
246 for (auto const& opt : vendor_options) {
247 option_vendor = boost::dynamic_pointer_cast<OptionVendor>(opt.second);
248 if (option_vendor) {
249 if (option_vendor->getVendorId() == ENTERPRISE_ID_ISC) {
250 break;
251 }
252 option_vendor.reset();
253 }
254 }
255
256 // If vendor option doesn't exist or its enterprise id is not ISC's
257 // enterprise id, let's create it.
258 if (!option_vendor) {
259 option_vendor.reset(new OptionVendor(Option::V6, ENTERPRISE_ID_ISC));
260 pkt->addOption(option_vendor);
261 }
262
263 // Push interface name and source address in it
264 option_vendor->addOption(OptionStringPtr(new OptionString(Option::V6,
265 ISC_V6_4O6_INTERFACE,
266 pkt->getIface())));
267 option_vendor->addOption(Option6AddrLstPtr(new Option6AddrLst(ISC_V6_4O6_SRC_ADDRESS,
268 pkt->getRemoteAddr())));
269 option_vendor->addOption(OptionUint16Ptr(new OptionUint16(Option::V6,
270 ISC_V6_4O6_SRC_PORT,
271 pkt->getRemotePort())));
272 // Get packet content
273 OutputBuffer& buf = pkt->getBuffer();
274 buf.clear();
275 pkt->pack();
276
277 // Try to send the message.
278 if (::send(socket_fd_, buf.getData(), buf.getLength(), 0) < 0) {
280 "failed to send DHCP4o6 message over the IPC: "
281 << strerror(errno));
282 }
283}
284
285} // namespace dhcp
286} // namespace isc
void send(const Pkt6Ptr &pkt)
Send message over IPC.
virtual ~Dhcp4o6IpcBase()
Destructor.
Pkt6Ptr receive()
Receive message over IPC.
uint16_t port_
Port number configured for IPC communication.
EndpointType
Endpoint type: DHCPv4 or DHCPv6 server.
Definition dhcp4o6_ipc.h:65
void close()
Close communication socket.
virtual void open()=0
Open communication socket (for derived classes).
Dhcp4o6IpcBase()
Constructor.
int socket_fd_
Socket descriptor.
Exception thrown when error occurs as a result of use of IPC.
Definition dhcp4o6_ipc.h:24
static IfaceMgr & instance()
IfaceMgr is a singleton class.
Definition iface_mgr.cc:54
DHCPv6 Option class for handling list of IPv6 addresses.
Option with defined data fields represented as buffers that can be accessed using data field index.
Class which represents an option carrying a single string value.
This class represents vendor-specific information option.
Represents a DHCPv6 packet.
Definition pkt6.h:44
The OutputBuffer class is a buffer abstraction for manipulating mutable data.
Definition buffer.h:343
void clear()
Clear buffer content.
Definition buffer.h:466
Defines the Dhcp4o6IpcBase class.
@ D6O_VENDOR_OPTS
Definition dhcp6.h:37
#define isc_throw(type, stream)
A shortcut macro to insert known values into exception arguments.
OptionInt< uint16_t > OptionUint16
Definition option_int.h:32
boost::shared_ptr< OptionUint16 > OptionUint16Ptr
Definition option_int.h:33
#define LOG_WARN(LOGGER, MESSAGE)
Macro to conveniently test warn output and log it.
Definition macros.h:26
boost::shared_ptr< OptionVendor > OptionVendorPtr
Pointer to a vendor option.
isc::log::Logger dhcpsrv_logger("dhcpsrv")
DHCP server library Logger.
Definition dhcpsrv_log.h:56
boost::shared_ptr< OptionCustom > OptionCustomPtr
A pointer to the OptionCustom object.
boost::shared_ptr< Iface > IfacePtr
Type definition for the pointer to an Iface object.
Definition iface_mgr.h:487
std::multimap< unsigned int, OptionPtr > OptionCollection
A collection of DHCP (v4 or v6) options.
Definition option.h:40
boost::shared_ptr< Option6AddrLst > Option6AddrLstPtr
Pointer to the Option6AddrLst object.
const isc::log::MessageID DHCPSRV_DHCP4O6_RECEIVED_BAD_PACKET
boost::shared_ptr< OptionString > OptionStringPtr
Pointer to the OptionString object.
boost::shared_ptr< Pkt6 > Pkt6Ptr
A pointer to Pkt6 packet.
Definition pkt6.h:31
Defines the logger used by the top-level component of kea-lfc.