1 // Copyright (C) 2014-2021 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 <dhcpsrv/dhcpsrv_log.h>
10 #include <dhcpsrv/cfg_iface.h>
11 #include <util/strutil.h>
12 #include <algorithm>
13 #include <functional>
14 
15 using namespace isc::asiolink;
16 using namespace isc::data;
17 namespace ph = std::placeholders;
18 
19 namespace isc {
20 namespace dhcp {
21 
22 const char* CfgIface::ALL_IFACES_KEYWORD = "*";
23 
CfgIface()24 CfgIface::CfgIface()
25     : wildcard_used_(false), socket_type_(SOCKET_RAW), re_detect_(false),
26       outbound_iface_(SAME_AS_INBOUND) {
27 }
28 
29 void
closeSockets() const30 CfgIface::closeSockets() const {
31     IfaceMgr::instance().closeSockets();
32 }
33 
34 bool
equals(const CfgIface & other) const35 CfgIface::equals(const CfgIface& other) const {
36     return (iface_set_ == other.iface_set_ &&
37             address_map_ == other.address_map_ &&
38             wildcard_used_ == other.wildcard_used_ &&
39             socket_type_ == other.socket_type_);
40 }
41 
42 bool
multipleAddressesPerInterfaceActive() const43 CfgIface::multipleAddressesPerInterfaceActive() const {
44     for (IfacePtr iface : IfaceMgr::instance().getIfaces()) {
45         if (iface->countActive4() > 1) {
46             return (true);
47         }
48     }
49     return (false);
50 }
51 
52 void
openSockets(const uint16_t family,const uint16_t port,const bool use_bcast) const53 CfgIface::openSockets(const uint16_t family, const uint16_t port,
54                       const bool use_bcast) const {
55     // Close any open sockets because we're going to modify some properties
56     // of the IfaceMgr. Those modifications require that sockets are closed.
57     closeSockets();
58     // The loopback interface can be used only when:
59     //  - UDP socket will be used, i.e. not IPv4 and RAW socket
60     //  - the loopback interface is in the interface set or the address map.
61     bool loopback_used_ = false;
62     if ((family == AF_INET6) || (socket_type_ == SOCKET_UDP)) {
63         // Check interface set
64         for (IfaceSet::const_iterator iface_name = iface_set_.begin();
65              iface_name != iface_set_.end(); ++iface_name) {
66             IfacePtr iface = IfaceMgr::instance().getIface(*iface_name);
67             if (iface && iface->flag_loopback_) {
68                 loopback_used_ = true;
69             }
70         }
71         // Check address map
72         for (ExplicitAddressMap::const_iterator unicast = address_map_.begin();
73              unicast != address_map_.end(); ++unicast) {
74             IfacePtr iface = IfaceMgr::instance().getIface(unicast->first);
75             if (iface && iface->flag_loopback_) {
76                 loopback_used_ = true;
77             }
78         }
79     }
80     // If wildcard interface '*' was not specified, set all interfaces to
81     // inactive state. We will later enable them selectively using the
82     // interface names specified by the user. If wildcard interface was
83     // specified, mark all interfaces active. Mark loopback inactive when
84     // not explicitly allowed.
85     setState(family, !wildcard_used_, !loopback_used_);
86     IfaceMgr& iface_mgr = IfaceMgr::instance();
87     // Remove selection of unicast addresses from all interfaces.
88     iface_mgr.clearUnicasts();
89     // Allow the loopback interface when required.
90     iface_mgr.setAllowLoopBack(loopback_used_);
91     // For the DHCPv4 server, if the user has selected that raw sockets
92     // should be used, we will try to configure the Interface Manager to
93     // support the direct responses to the clients that don't have the
94     // IP address. This should effectively turn on the use of raw
95     // sockets. However, this may be unsupported on some operating
96     // systems, so there is no guarantee.
97     if ((family == AF_INET) && (!IfaceMgr::instance().isTestMode())) {
98         iface_mgr.setMatchingPacketFilter(socket_type_ == SOCKET_RAW);
99         if ((socket_type_ == SOCKET_RAW) &&
100             !iface_mgr.isDirectResponseSupported()) {
101             LOG_WARN(dhcpsrv_logger, DHCPSRV_CFGMGR_SOCKET_RAW_UNSUPPORTED);
102         }
103     }
104     // If there is no wildcard interface specified, we will have to iterate
105     // over the names specified by the caller and enable them.
106     if (!wildcard_used_) {
107         for (IfaceSet::const_iterator iface_name = iface_set_.begin();
108              iface_name != iface_set_.end(); ++iface_name) {
109             IfacePtr iface = IfaceMgr::instance().getIface(*iface_name);
110             // This shouldn't really happen because we are checking the
111             // names of interfaces when they are being added (use()
112             // function). But, if someone has triggered detection of
113             // interfaces since then, some interfaces may have disappeared.
114             if (iface == NULL) {
115                 isc_throw(Unexpected,
116                           "fail to open socket on interface '"
117                           << *iface_name << "' as this interface doesn't"
118                           " exist");
119 
120             } else if (family == AF_INET) {
121                 iface->inactive4_ = false;
122                 setIfaceAddrsState(family, true, *iface);
123 
124             } else {
125                 iface->inactive6_ = false;
126             }
127         }
128     }
129 
130     // Select unicast sockets for DHCPv6 or activate specific IPv4 addresses
131     // for DHCPv4.
132     for (ExplicitAddressMap::const_iterator unicast = address_map_.begin();
133          unicast != address_map_.end(); ++unicast) {
134         IfacePtr iface = IfaceMgr::instance().getIface(unicast->first);
135         if (iface == NULL) {
136             isc_throw(Unexpected,
137                       "fail to open unicast socket on interface '"
138                       << unicast->first << "' as this interface doesn't"
139                       " exist");
140         }
141         if (family == AF_INET6) {
142             iface->addUnicast(unicast->second);
143             iface->inactive6_ = false;
144 
145         } else {
146             iface->setActive(unicast->second, true);
147             iface->inactive4_ = false;
148         }
149     }
150 
151     // Set the callback which is called when the socket fails to open
152     // for some specific interface. This callback will simply log a
153     // warning message.
154     IfaceMgrErrorMsgCallback error_callback =
155         std::bind(&CfgIface::socketOpenErrorHandler, ph::_1);
156     bool sopen;
157     if (family == AF_INET) {
158         // Use broadcast only if we're using raw sockets. For the UDP sockets,
159         // we only handle the relayed (unicast) traffic.
160         const bool can_use_bcast = use_bcast && (socket_type_ == SOCKET_RAW);
161         // Opening multiple raw sockets handling brodcast traffic on the single
162         // interface may lead to processing the same message multiple times.
163         // We don't prohibit such configuration because raw sockets can as well
164         // handle the relayed traffic. We have to issue a warning, however, to
165         // draw administrator's attention.
166         if (can_use_bcast && multipleAddressesPerInterfaceActive()) {
167             LOG_WARN(dhcpsrv_logger, DHCPSRV_MULTIPLE_RAW_SOCKETS_PER_IFACE);
168         }
169         sopen = IfaceMgr::instance().openSockets4(port, can_use_bcast, error_callback);
170     } else {
171         // use_bcast is ignored for V6.
172         sopen = IfaceMgr::instance().openSockets6(port, error_callback);
173     }
174 
175     if (!sopen) {
176         // If no socket were opened, log a warning because the server will
177         // not respond to any queries.
178         LOG_WARN(dhcpsrv_logger, DHCPSRV_NO_SOCKETS_OPEN);
179     }
180 }
181 
182 void
reset()183 CfgIface::reset() {
184     wildcard_used_ = false;
185     iface_set_.clear();
186     address_map_.clear();
187     useSocketType(AF_INET, SOCKET_RAW);
188 }
189 
190 void
setState(const uint16_t family,const bool inactive,const bool loopback_inactive) const191 CfgIface::setState(const uint16_t family, const bool inactive,
192                    const bool loopback_inactive) const {
193     for (IfacePtr iface : IfaceMgr::instance().getIfaces()) {
194         bool iface_inactive = iface->flag_loopback_ ? loopback_inactive : inactive;
195         if (family == AF_INET) {
196             iface->inactive4_ = iface_inactive;
197         } else {
198             iface->inactive6_ = iface_inactive;
199         }
200 
201         // Activate/deactivate all addresses.
202         setIfaceAddrsState(family, !inactive, *iface);
203     }
204 }
205 
206 void
setIfaceAddrsState(const uint16_t family,const bool active,Iface & iface) const207 CfgIface::setIfaceAddrsState(const uint16_t family, const bool active,
208                              Iface& iface) const {
209     // Activate/deactivate all addresses.
210     for (Iface::Address addr : iface.getAddresses()) {
211         if (addr.get().getFamily() == family) {
212             iface.setActive(addr.get(), active);
213         }
214     }
215 }
216 
217 void
socketOpenErrorHandler(const std::string & errmsg)218 CfgIface::socketOpenErrorHandler(const std::string& errmsg) {
219     LOG_WARN(dhcpsrv_logger, DHCPSRV_OPEN_SOCKET_FAIL).arg(errmsg);
220 }
221 
222 std::string
socketTypeToText() const223 CfgIface::socketTypeToText() const {
224     switch (socket_type_) {
225     case SOCKET_RAW:
226         return ("raw");
227 
228     case SOCKET_UDP:
229         return ("udp");
230 
231     default:
232         ;
233     }
234 
235     isc_throw(Unexpected, "unsupported socket type " << socket_type_);
236 }
237 
238 CfgIface::SocketType
textToSocketType(const std::string & socket_type_name) const239 CfgIface::textToSocketType(const std::string& socket_type_name) const {
240     if (socket_type_name == "udp") {
241         return (SOCKET_UDP);
242 
243     } else if (socket_type_name == "raw") {
244         return (SOCKET_RAW);
245 
246     } else {
247         isc_throw(InvalidSocketType, "unsupported socket type '"
248                   << socket_type_name << "'");
249     }
250 }
251 
252 CfgIface::OutboundIface
getOutboundIface() const253 CfgIface::getOutboundIface() const {
254     return (outbound_iface_);
255 }
256 
257 std::string
outboundTypeToText() const258 CfgIface::outboundTypeToText() const {
259     switch (outbound_iface_) {
260     case SAME_AS_INBOUND:
261         return ("same-as-inbound");
262     case USE_ROUTING:
263         return ("use-routing");
264     default:
265         isc_throw(Unexpected, "unsupported outbound-type " << socket_type_);
266     }
267 
268 }
269 
270 CfgIface::OutboundIface
textToOutboundIface(const std::string & txt)271 CfgIface::textToOutboundIface(const std::string& txt) {
272     if (txt == "same-as-inbound") {
273         return (SAME_AS_INBOUND);
274 
275     } else if (txt == "use-routing") {
276         return (USE_ROUTING);
277 
278     } else {
279         isc_throw(BadValue, "unsupported outbound interface type '"
280                   << txt << "'");
281     }
282 }
283 
284 void
setOutboundIface(const OutboundIface & outbound_iface)285 CfgIface::setOutboundIface(const OutboundIface& outbound_iface) {
286     outbound_iface_ = outbound_iface;
287 }
288 
289 void
use(const uint16_t family,const std::string & iface_name)290 CfgIface::use(const uint16_t family, const std::string& iface_name) {
291     // The interface name specified may have two formats:
292     // - "interface-name", e.g. eth0
293     // - "interface-name/address", e.g. eth0/10.0.0.1 or eth/2001:db8:1::1
294     // The latter format is used to open unicast socket on the specified
295     // interface. Here we are detecting which format was used and we strip
296     // all extraneous spaces.
297     size_t pos = iface_name.find("/");
298     std::string name;
299     std::string addr_str;
300     // There is no unicast address so the whole string is an interface name.
301     if (pos == std::string::npos) {
302         name = util::str::trim(iface_name);
303         if (name.empty()) {
304             isc_throw(InvalidIfaceName,
305                       "empty interface name used in configuration");
306 
307         } else if (name != ALL_IFACES_KEYWORD) {
308             if (IfaceMgr::instance().getIface(name) == NULL) {
309                 isc_throw(NoSuchIface, "interface '" << name
310                           << "' doesn't exist in the system");
311             }
312 
313         } else if (wildcard_used_) {
314             isc_throw(DuplicateIfaceName, "the wildcard interface '"
315                       << ALL_IFACES_KEYWORD << "' can only be specified once");
316 
317         } else {
318             LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE,
319                       DHCPSRV_CFGMGR_ALL_IFACES_ACTIVE);
320             wildcard_used_ = true;
321 
322         }
323 
324     } else {
325         // The interface name includes the address on which the socket should
326         // be opened, and we need to split interface name and the address to
327         // two variables.
328         name = util::str::trim(iface_name.substr(0, pos));
329         addr_str = util::str::trim(iface_name.substr(pos + 1));
330 
331         // Interface name must not be empty.
332         if (name.empty()) {
333             isc_throw(InvalidIfaceName,
334                       "empty interface name specified in the"
335                       " interface configuration");
336 
337         }
338         // An address following the interface name must not be empty.
339         if (addr_str.empty()) {
340             isc_throw(InvalidIfaceName,
341                       "empty address specified in the interface"
342                       << " configuration");
343 
344         }
345 
346         // Interface name must not be the wildcard name.
347         if (name == ALL_IFACES_KEYWORD) {
348             isc_throw(InvalidIfaceName,
349                       "wildcard interface name '" << ALL_IFACES_KEYWORD
350                       << "' must not be used in conjunction with an"
351                       " address");
352 
353         }
354 
355         // Interface must exist.
356         IfacePtr iface = IfaceMgr::instance().getIface(name);
357         if (!iface) {
358             isc_throw(NoSuchIface, "interface '" << name
359                       << "' doesn't exist in the system");
360 
361         }
362 
363         // Convert address string. This may throw an exception if the address
364         // is invalid.
365         IOAddress addr(addr_str);
366 
367         // Validate V6 address.
368         if (family == AF_INET6) {
369             // Check that the address is a valid unicast address.
370             if (!addr.isV6() || addr.isV6Multicast()) {
371                 isc_throw(InvalidIfaceName, "address '" << addr << "' is not"
372                           " a valid IPv6 unicast address");
373             }
374 
375             // There are valid cases where link local address can be specified to
376             // receive unicast traffic, e.g. sent by relay agent.
377             if (addr.isV6LinkLocal()) {
378                 LOG_WARN(dhcpsrv_logger, DHCPSRV_CFGMGR_UNICAST_LINK_LOCAL)
379                     .arg(addr.toText()).arg(name);
380             }
381 
382         } else {
383             if (!addr.isV4()) {
384                 isc_throw(InvalidIfaceName, "address '" << addr << "' is not"
385                           " a valid IPv4 address");
386             }
387         }
388 
389         // Interface must have this address assigned.
390         if (!iface->hasAddress(addr)) {
391             isc_throw(NoSuchAddress,
392                       "interface '" << name << "' doesn't have address '"
393                       << addr << "' assigned");
394         }
395 
396         // For the IPv4, if the interface name was specified (instead of the interface-
397         // address tuple) all addresses are already activated. Adding an explicit address
398         // for the interface should result in error.
399         if ((family == AF_INET) && (iface_set_.find(iface->getName()) != iface_set_.end())) {
400             isc_throw(DuplicateIfaceName, "interface '" << iface->getName()
401                       << "' has already been selected");
402         }
403 
404         // Check if the address hasn't been selected already.
405         std::pair<const std::string, IOAddress> iface_address_tuple(name, addr);
406         if (std::find(address_map_.begin(), address_map_.end(),
407                       iface_address_tuple) != address_map_.end()) {
408             isc_throw(DuplicateAddress, "must not select address '"
409                       << addr << "' for interface '" << name << "' "
410                       "because this address is already selected");
411         }
412 
413         if (family == AF_INET6) {
414             LOG_INFO(dhcpsrv_logger, DHCPSRV_CFGMGR_USE_UNICAST)
415                 .arg(addr.toText()).arg(name);
416 
417         } else {
418             LOG_INFO(dhcpsrv_logger, DHCPSRV_CFGMGR_USE_ADDRESS)
419                 .arg(addr.toText()).arg(name);
420         }
421         address_map_.insert(std::pair<std::string, IOAddress>(name, addr));
422     }
423 
424     // If interface name was explicitly specified without an address, we will
425     // insert the interface name to the set of enabled interfaces.
426     if ((name != ALL_IFACES_KEYWORD) && addr_str.empty()) {
427         // An interface has been selected or an IPv4 address on this interface
428         // has been selected it is not allowed to select the whole interface.
429         if ((iface_set_.find(name) != iface_set_.end()) ||
430             ((family == AF_INET) && address_map_.count(name) > 0)) {
431             isc_throw(DuplicateIfaceName, "interface '" << name
432                       << "' has already been specified");
433         }
434 
435         // Log that we're listening on the specific interface and that the
436         // address is not explicitly specified.
437         if (addr_str.empty()) {
438             LOG_INFO(dhcpsrv_logger, DHCPSRV_CFGMGR_ADD_IFACE).arg(name);
439         }
440         iface_set_.insert(name);
441     }
442 }
443 
444 void
useSocketType(const uint16_t family,const SocketType & socket_type)445 CfgIface::useSocketType(const uint16_t family,
446                         const SocketType& socket_type) {
447     if (family != AF_INET) {
448         isc_throw(InvalidSocketType, "socket type must not be specified for"
449                   " the DHCPv6 server");
450     }
451     socket_type_ = socket_type;
452     LOG_INFO(dhcpsrv_logger, DHCPSRV_CFGMGR_SOCKET_TYPE_SELECT)
453         .arg(socketTypeToText());
454 }
455 
456 void
useSocketType(const uint16_t family,const std::string & socket_type_name)457 CfgIface::useSocketType(const uint16_t family,
458                         const std::string& socket_type_name) {
459     useSocketType(family, textToSocketType(socket_type_name));
460 }
461 
462 ElementPtr
toElement() const463 CfgIface::toElement() const {
464     ElementPtr result = Element::createMap();
465 
466     // Set user context
467     contextToElement(result);
468 
469     // Set interfaces
470     ElementPtr ifaces = Element::createList();
471     if (wildcard_used_) {
472         ifaces->add(Element::create(std::string(ALL_IFACES_KEYWORD)));
473     }
474     for (IfaceSet::const_iterator iface = iface_set_.cbegin();
475          iface != iface_set_.cend(); ++iface) {
476         ifaces->add(Element::create(*iface));
477     }
478     for (ExplicitAddressMap::const_iterator address = address_map_.cbegin();
479          address != address_map_.cend(); ++address) {
480         std::string spec = address->first + "/" + address->second.toText();
481         ifaces->add(Element::create(spec));
482     }
483     result->set("interfaces", ifaces);
484 
485     // Set dhcp-socket-type (no default because it is DHCPv4 specific)
486     // @todo emit raw if and only if DHCPv4
487     if (socket_type_ != SOCKET_RAW) {
488         result->set("dhcp-socket-type", Element::create(std::string("udp")));
489     }
490 
491     if (outbound_iface_ != SAME_AS_INBOUND) {
492         result->set("outbound-interface", Element::create(outboundTypeToText()));
493     }
494 
495     // Set re-detect
496     result->set("re-detect", Element::create(re_detect_));
497 
498     return (result);
499 }
500 
501 } // end of isc::dhcp namespace
502 } // end of isc namespace
503