dhcp4o6_ipc.cc 7.66 KB
Newer Older
1
// Copyright (C) 2015-2016 Internet Systems Consortium, Inc. ("ISC")
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
//
// Permission to use, copy, modify, and/or distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
// copyright notice and this permission notice appear in all copies.
//
// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
// PERFORMANCE OF THIS SOFTWARE.

#include <config.h>

17
#include <dhcp/dhcp6.h>
18
#include <dhcp/iface_mgr.h>
19
20
21
#include <dhcp/option6_addrlst.h>
#include <dhcp/option_string.h>
#include <dhcp/option_vendor.h>
22
#include <dhcpsrv/dhcp4o6_ipc.h>
23
#include <dhcpsrv/dhcpsrv_log.h>
24
25

#include <netinet/in.h>
26
#include <sys/fcntl.h>
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
#include <string>

using namespace isc::asiolink;
using namespace isc::util;
using namespace std;

namespace isc {
namespace dhcp {

Dhcp4o6IpcBase::Dhcp4o6IpcBase() : port_(0), socket_fd_(-1) {}

Dhcp4o6IpcBase::~Dhcp4o6IpcBase() {
    close();
}

int Dhcp4o6IpcBase::open(uint16_t port, int side) {
    if (port == port_) {
        // No change: nothing to do
        return (socket_fd_);
    }

    // Port 0: closing
    if (port == 0) {
        port_ = 0;
        if (socket_fd_ != -1) {
            IfaceMgr::instance().deleteExternalSocket(socket_fd_);
            ::close(socket_fd_);
            socket_fd_ = -1;
        }
        return (socket_fd_);
    }

    // Open socket
    int sock = socket(PF_INET6, SOCK_DGRAM, IPPROTO_UDP);
    if (sock < 0) {
        isc_throw(Unexpected, "Failed to create DHCP4o6 socket.");
    }

    // Set reuse address
    int flag = 1;
    if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR,
                   (char *)&flag, sizeof(flag)) < 0) {
        ::close(sock);
        isc_throw(Unexpected, "Failed to set SO_REUSEADDR on DHCP4o6 socket.");
    }

    // Set no blocking
    if (fcntl(sock, F_SETFL, O_NONBLOCK) < 0) {
        ::close(sock);
        isc_throw(Unexpected, "Failed to set O_NONBLOCK on DHCP4o6 socket.");
    }

    // Bind to the local address
    struct sockaddr_in6 local6;
    memset(&local6, 0, sizeof(local6));
    local6.sin6_family = AF_INET6;
#ifdef HAVE_SA_LEN
    local6.sin6_len = sizeof(local6);
#endif
    if (side == 6) {
        local6.sin6_port = htons(port);
    } else {
        local6.sin6_port = htons(port + 1);
    }
Francis Dupont's avatar
Francis Dupont committed
91
92
    // We'll connect to the loopback address so bind to it too.
    local6.sin6_addr.s6_addr[15] = 1;
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
    if (bind(sock, (struct sockaddr *)&local6, sizeof(local6)) < 0) {
        ::close(sock);
        isc_throw(Unexpected, "Failed to bind DHCP4o6 socket.");
    }

    // Connect to the remote address
    struct sockaddr_in6 remote6;
    memset(&remote6, 0, sizeof(remote6));
    remote6.sin6_family = AF_INET6;
#ifdef HAVE_SA_LEN
    remote6.sin6_len = sizeof(remote6);
#endif
    if (side == 6) {
        remote6.sin6_port = htons(port + 1);
    } else {
        remote6.sin6_port = htons(port);
    }
110
111
112
113
114
    // At least OpenBSD requires the remote address to not be left
    // unspecified, so we set it to the loopback address.
    remote6.sin6_addr.s6_addr[15] = 1;
    if (connect(sock, reinterpret_cast<const struct sockaddr*>(&remote6),
                sizeof(remote6)) < 0) {
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
        ::close(sock);
        isc_throw(Unexpected, "Failed to connect DHCP4o6 socket.");
    }

    if (socket_fd_ != -1) {
        if (dup2(sock, socket_fd_) == -1) {
            ::close(sock);
            isc_throw(Unexpected, "Failed to duplicate DHCP4o6 socket.");
        }
        if (sock != socket_fd_) {
            ::close(sock);
            sock = socket_fd_;
        }
    }

    // Success
    port_ = port;
    socket_fd_ = sock;
    return (socket_fd_);
}

void Dhcp4o6IpcBase::close() {
    static_cast<void>(open(0, 0));
}

Pkt6Ptr Dhcp4o6IpcBase::receive() {
    uint8_t buf[65536];
    ssize_t cc = recv(socket_fd_, buf, sizeof(buf), 0);
    if (cc < 0) {
        isc_throw(Unexpected, "Failed to receive on DHCP4o6 socket.");
    }
146
147
148
149
150
151
152
    Pkt6Ptr pkt = Pkt6Ptr(new Pkt6(buf, cc));
    pkt->updateTimestamp();

    // Get interface name and remote address
    pkt->unpack();
    OptionVendorPtr vendor =
        boost::dynamic_pointer_cast<OptionVendor>(pkt->getOption(D6O_VENDOR_OPTS));
153
154
155
156
157
158
159
160
161
162
    if (!vendor) {
        LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL,
                  DHCPSRV_DHCP4O6_RECEIVED_BAD_PACKET)
            .arg("no vendor option");
        return (Pkt6Ptr());
    }
    if (vendor->getVendorId() != ENTERPRISE_ID_ISC) {
        LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL,
                  DHCPSRV_DHCP4O6_RECEIVED_BAD_PACKET)
            .arg("vendor option enterprise ID is not ISC");
163
164
165
166
167
        return (Pkt6Ptr());
    }
    OptionStringPtr ifname =
        boost::dynamic_pointer_cast<OptionString>(vendor->getOption(ISC_V6_4O6_INTERFACE));
    if (!ifname) {
168
169
170
        LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL,
                  DHCPSRV_DHCP4O6_RECEIVED_BAD_PACKET)
            .arg("no interface suboption");
171
172
        return (Pkt6Ptr());
    }
173
    IfacePtr iface = IfaceMgr::instance().getIface(ifname->getValue());
174
    if (!iface) {
175
176
177
        LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL,
                  DHCPSRV_DHCP4O6_RECEIVED_BAD_PACKET)
            .arg("can't get interface");
178
179
        return (Pkt6Ptr());
    }
180
181
182
    Option6AddrLstPtr srcs =
        boost::dynamic_pointer_cast<Option6AddrLst>(vendor->getOption(ISC_V6_4O6_SRC_ADDRESS));
    if (!srcs) {
183
184
185
        LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL,
                  DHCPSRV_DHCP4O6_RECEIVED_BAD_PACKET)
            .arg("no source address suboption");
186
187
188
189
        return (Pkt6Ptr());
    }
    Option6AddrLst::AddressContainer addrs = srcs->getAddresses();
    if (addrs.size() != 1) {
190
191
192
        LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL,
                  DHCPSRV_DHCP4O6_RECEIVED_BAD_PACKET)
            .arg("bad source address suboption");
193
194
195
196
197
198
        return (Pkt6Ptr());
    }

    // Update the packet and return it
    static_cast<void>(pkt->delOption(D6O_VENDOR_OPTS));
    pkt->setRemoteAddr(addrs[0]);
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
    pkt->setIface(iface->getName());
    pkt->setIndex(iface->getIndex());
    return (pkt);
}

void Dhcp4o6IpcBase::send(Pkt6Ptr pkt) {
    // No packet: nothing to send
    if (!pkt) {
        return;
    }

    // Disabled: nowhere to send
    if (socket_fd_ == -1) {
        return;
    }

215
    // Check if vendor option exists.
216
    OptionVendorPtr vendor_opt = boost::dynamic_pointer_cast<
217
218
219
220
        OptionVendor>(pkt->getOption(D6O_VENDOR_OPTS));

    // If vendor option doesn't exist or its enterprise id is not ISC's
    // enterprise id, let's create it.
221
222
    if (!vendor_opt || (vendor_opt->getVendorId() != ENTERPRISE_ID_ISC)) {
        vendor_opt.reset(new OptionVendor(Option::V6, ENTERPRISE_ID_ISC));
223
224

    }
225

226
    // Push interface name and source address in it
227
228
229
230
231
232
    vendor_opt->addOption(OptionPtr(new OptionString(Option::V6,
                                                     ISC_V6_4O6_INTERFACE,
                                                     pkt->getIface())));
    vendor_opt->addOption(OptionPtr(new Option6AddrLst(ISC_V6_4O6_SRC_ADDRESS,
                                                       pkt->getRemoteAddr())));
    pkt->addOption(vendor_opt);
233
234

    // Get packet content
235
236
237
    OutputBuffer& buf = pkt->getBuffer();
    buf.clear();
    pkt->pack();
238
239

    // Send
240
    static_cast<void>(::send(socket_fd_, buf.getData(), buf.getLength(), 0));
241
242
243
244
245
246
    return;
}

};  // namespace dhcp

};  // namespace isc