dhcp4o6_ipc.cc 5.98 KB
Newer Older
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// Copyright (C) 2015 Internet Systems Consortium, Inc. ("ISC")
//
// 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
23
24
25
26
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
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
#include <dhcpsrv/dhcp4o6_ipc.h>

#include <netinet/in.h>
#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);
    }
    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);
    }
    if (connect(sock, (struct sockaddr *)&remote6, sizeof(remote6)) < 0) {
        ::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.");
    }
138
139
140
141
142
143
144
145
146
147
148
149
150
    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));
    if (!vendor || vendor->getVendorId() != ENTERPRISE_ID_ISC) {
        return (Pkt6Ptr());
    }
    OptionStringPtr ifname =
        boost::dynamic_pointer_cast<OptionString>(vendor->getOption(ISC_V6_4O6_INTERFACE));
    if (!ifname) {
151
152
        return (Pkt6Ptr());
    }
153
    IfacePtr iface = IfaceMgr::instance().getIface(ifname->getValue());
154
155
156
    if (!iface) {
        return (Pkt6Ptr());
    }
157
158
159
160
161
162
163
164
165
166
167
168
169
    Option6AddrLstPtr srcs =
        boost::dynamic_pointer_cast<Option6AddrLst>(vendor->getOption(ISC_V6_4O6_SRC_ADDRESS));
    if (!srcs) {
        return (Pkt6Ptr());
    }
    Option6AddrLst::AddressContainer addrs = srcs->getAddresses();
    if (addrs.size() != 1) {
        return (Pkt6Ptr());
    }

    // Update the packet and return it
    static_cast<void>(pkt->delOption(D6O_VENDOR_OPTS));
    pkt->setRemoteAddr(addrs[0]);
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
    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;
    }

186
187
    // Get a vendor option
    OptionVendorPtr vendor(new OptionVendor(Option::V6, ENTERPRISE_ID_ISC));
188

189
190
191
192
193
194
195
    // Push interface name and source address in it
    vendor->addOption(OptionPtr(new OptionString(Option::V6,
                                                 ISC_V6_4O6_INTERFACE,
                                                 pkt->getIface())));
    vendor->addOption(OptionPtr(new Option6AddrLst(ISC_V6_4O6_SRC_ADDRESS,
                                                   pkt->getRemoteAddr())));
    pkt->addOption(vendor);
196
197

    // Get packet content
198
199
200
    OutputBuffer& buf = pkt->getBuffer();
    buf.clear();
    pkt->pack();
201
202

    // Send
203
    static_cast<void>(::send(socket_fd_, buf.getData(), buf.getLength(), 0));
204
205
206
207
208
209
    return;
}

};  // namespace dhcp

};  // namespace isc