pkt.cc 8.13 KB
Newer Older
Tomek Mrugalski's avatar
Tomek Mrugalski committed
1
// Copyright (C) 2014-2015 Internet Systems Consortium, Inc. ("ISC")
2
3
4
5
6
7
8
9
10
11
12
13
14
//
// 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.

15
#include <config.h>
16
17
#include <utility>
#include <dhcp/pkt.h>
18
#include <dhcp/iface_mgr.h>
Tomek Mrugalski's avatar
Tomek Mrugalski committed
19
#include <dhcp/hwaddr.h>
20
#include <vector>
21
22
23
24

namespace isc {
namespace dhcp {

Tomek Mrugalski's avatar
Tomek Mrugalski committed
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
Pkt::Pkt(uint32_t transid, const isc::asiolink::IOAddress& local_addr,
         const isc::asiolink::IOAddress& remote_addr, uint16_t local_port,
         uint16_t remote_port)
    :transid_(transid),
     iface_(""),
     ifindex_(-1),
     local_addr_(local_addr),
     remote_addr_(remote_addr),
     local_port_(local_port),
     remote_port_(remote_port),
     buffer_out_(0)
{
}

Pkt::Pkt(const uint8_t* buf, uint32_t len, const isc::asiolink::IOAddress& local_addr,
         const isc::asiolink::IOAddress& remote_addr, uint16_t local_port,
         uint16_t remote_port)
    :transid_(0),
     iface_(""),
     ifindex_(-1),
     local_addr_(local_addr),
     remote_addr_(remote_addr),
     local_port_(local_port),
     remote_port_(remote_port),
     buffer_out_(0)
{
    data_.resize(len);
52
53
54
    if (len) {
        memcpy(&data_[0], buf, len);
    }
55
56
}

Tomek Mrugalski's avatar
Tomek Mrugalski committed
57
58
59
60
61
void
Pkt::addOption(const OptionPtr& opt) {
    options_.insert(std::pair<int, OptionPtr>(opt->getType(), opt));
}

62
63
64
65
66
67
OptionPtr
Pkt::getOption(uint16_t type) const {
    OptionCollection::const_iterator x = options_.find(type);
    if (x != options_.end()) {
        return (*x).second;
    }
68
    return (OptionPtr()); // NULL
69
70
71
72
}

bool
Pkt::delOption(uint16_t type) {
Tomek Mrugalski's avatar
Tomek Mrugalski committed
73

74
75
76
77
    isc::dhcp::OptionCollection::iterator x = options_.find(type);
    if (x!=options_.end()) {
        options_.erase(x);
        return (true); // delete successful
Tomek Mrugalski's avatar
Tomek Mrugalski committed
78
79
    } else {
        return (false); // can't find option to be deleted
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
    }
}

bool
Pkt::inClass(const std::string& client_class) {
    return (classes_.find(client_class) != classes_.end());
}

void
Pkt::addClass(const std::string& client_class) {
    if (classes_.find(client_class) == classes_.end()) {
        classes_.insert(client_class);
    }
}

void
Pkt::updateTimestamp() {
    timestamp_ = boost::posix_time::microsec_clock::universal_time();
}

void Pkt::repack() {
101
102
103
    if (!data_.empty()) {
        buffer_out_.writeData(&data_[0], data_.size());
    }
104
105
}

106
107
void
Pkt::setRemoteHWAddr(const uint8_t htype, const uint8_t hlen,
Tomek Mrugalski's avatar
Tomek Mrugalski committed
108
109
                      const std::vector<uint8_t>& hw_addr) {
    setHWAddrMember(htype, hlen, hw_addr, remote_hwaddr_);
110
111
112
}

void
Tomek Mrugalski's avatar
Tomek Mrugalski committed
113
114
Pkt::setRemoteHWAddr(const HWAddrPtr& hw_addr) {
    if (!hw_addr) {
115
116
117
        isc_throw(BadValue, "Setting remote HW address to NULL is"
                  << " forbidden.");
    }
Tomek Mrugalski's avatar
Tomek Mrugalski committed
118
    remote_hwaddr_ = hw_addr;
119
120
121
122
}

void
Pkt::setHWAddrMember(const uint8_t htype, const uint8_t,
123
124
                      const std::vector<uint8_t>& hw_addr,
                      HWAddrPtr& storage) {
125

126
    storage.reset(new HWAddr(hw_addr, htype));
127
128
}

129
HWAddrPtr
130
Pkt::getMAC(uint32_t hw_addr_src) {
131
    HWAddrPtr mac;
132

Tomek Mrugalski's avatar
Tomek Mrugalski committed
133
134
    /// @todo: Implement an array of method pointers instead of set of ifs

135
    // Method 1: from raw sockets.
Tomek Mrugalski's avatar
Tomek Mrugalski committed
136
    if (hw_addr_src & HWAddr::HWADDR_SOURCE_RAW) {
137
138
139
        mac = getRemoteHWAddr();
        if (mac) {
            return (mac);
Tomek Mrugalski's avatar
Tomek Mrugalski committed
140
        } else if (hw_addr_src == HWAddr::HWADDR_SOURCE_RAW) {
Tomek Mrugalski's avatar
Tomek Mrugalski committed
141
142
            // If we're interested only in RAW sockets as source of that info,
            // there's no point in trying other options.
143
            return (HWAddrPtr());
144
145
146
        }
    }

147
    // Method 2: From client link-layer address option inserted by a relay
Tomek Mrugalski's avatar
Tomek Mrugalski committed
148
    if (hw_addr_src & HWAddr::HWADDR_SOURCE_CLIENT_ADDR_RELAY_OPTION) {
149
150
151
        mac = getMACFromIPv6RelayOpt();
        if (mac) {
            return (mac);
Tomek Mrugalski's avatar
Tomek Mrugalski committed
152
        } else if (hw_addr_src ==  HWAddr::HWADDR_SOURCE_CLIENT_ADDR_RELAY_OPTION) {
153
154
155
156
157
158
159
            // If we're interested only in RFC6939 link layer address as source
            // of that info, there's no point in trying other options.
            return (HWAddrPtr());
        }
    }

    // Method 3: Extracted from DUID-LLT or DUID-LL
Tomek Mrugalski's avatar
Tomek Mrugalski committed
160
    if(hw_addr_src & HWAddr::HWADDR_SOURCE_DUID) {
161
        mac = getMACFromDUID();
162
        if (mac) {
163
            return (mac);
Tomek Mrugalski's avatar
Tomek Mrugalski committed
164
        } else if (hw_addr_src == HWAddr::HWADDR_SOURCE_DUID) {
165
166
167
168
            // If the only source allowed is DUID then we can skip the other
            // methods.
            return (HWAddrPtr());
        }
169
    }
170

171
    // Method 4: Extracted from source IPv6 link-local address
Tomek Mrugalski's avatar
Tomek Mrugalski committed
172
    if (hw_addr_src & HWAddr::HWADDR_SOURCE_IPV6_LINK_LOCAL) {
173
174
175
        mac = getMACFromSrcLinkLocalAddr();
        if (mac) {
            return (mac);
Tomek Mrugalski's avatar
Tomek Mrugalski committed
176
        } else if (hw_addr_src ==  HWAddr::HWADDR_SOURCE_IPV6_LINK_LOCAL) {
177
178
179
180
181
182
183
            // If we're interested only in link-local addr as source of that
            // info, there's no point in trying other options.
            return (HWAddrPtr());
        }
    }

    // Method 5: From remote-id option inserted by a relay
184
185
186
187
188
189
190
191
192
193
    if(hw_addr_src & HWAddr::HWADDR_SOURCE_REMOTE_ID) {
        mac = getMACFromRemoteIdRelayOption();
        if (mac) {
            return (mac);
        } else if (hw_addr_src == HWAddr::HWADDR_SOURCE_REMOTE_ID) {
            // If the only source allowed is remote-id option then we can skip
            // the other methods.
            return (HWAddrPtr());
        }
    }
194

Francis Dupont's avatar
Francis Dupont committed
195
    // Method 6: From subscriber-id option inserted by a relay
196
197

    // Method 7: From docsis options
198
    if (hw_addr_src & HWAddr::HWADDR_SOURCE_DOCSIS_CMTS) {
199
200
201
        mac = getMACFromDocsisCMTS();
        if (mac) {
            return (mac);
202
        } else if (hw_addr_src == HWAddr::HWADDR_SOURCE_DOCSIS_CMTS) {
203
204
205
206
207
            // If we're interested only in CMTS options as a source of that
            // info, there's no point in trying other options.
            return (HWAddrPtr());
        }
    }
208

209
    // Method 8: From docsis options
210
    if (hw_addr_src & HWAddr::HWADDR_SOURCE_DOCSIS_MODEM) {
211
212
213
        mac = getMACFromDocsisModem();
        if (mac) {
            return (mac);
214
        } else if (hw_addr_src == HWAddr::HWADDR_SOURCE_DOCSIS_MODEM) {
215
216
217
218
219
            // If we're interested only in CMTS options as a source of that
            // info, there's no point in trying other options.
            return (HWAddrPtr());
        }
    }
220
221
222
223
224

    // Ok, none of the methods were suitable. Return NULL.
    return (HWAddrPtr());
}

225
HWAddrPtr
Tomek Mrugalski's avatar
Tomek Mrugalski committed
226
Pkt::getMACFromIPv6(const isc::asiolink::IOAddress& addr) {
227
228

    if (!addr.isV6LinkLocal()) {
229
230
231
        return (HWAddrPtr());
    }

232
    std::vector<uint8_t> bin = addr.toBytes();
233
234
235
236
237
238
239

    // Double check that it's of appropriate size
    if ((bin.size() != isc::asiolink::V6ADDRESS_LEN) ||

        // Check that it's link-local (starts with fe80).
        (bin[0] != 0xfe) || (bin[1] != 0x80) ||

Tomek Mrugalski's avatar
Tomek Mrugalski committed
240
241
        // Check that u bit is set and g is clear. See Section 2.5.1 of RFC2373
        // for details.
Francis Dupont's avatar
Francis Dupont committed
242
        ((bin[8] & 3) != 2) ||
Tomek Mrugalski's avatar
Tomek Mrugalski committed
243

244
245
246
247
248
249
250
251
252
253
254
        // And that the IID is of EUI-64 type.
        (bin[11] != 0xff) || (bin[12] != 0xfe)) {
        return (HWAddrPtr());
    }

    // Remove 8 most significant bytes
    bin.erase(bin.begin(), bin.begin() + 8);

    // Ok, we're down to EUI-64 only now: XX:XX:XX:ff:fe:XX:XX:XX
    bin.erase(bin.begin() + 3, bin.begin() + 5);

Tomek Mrugalski's avatar
Tomek Mrugalski committed
255
256
257
258
    // MAC-48 to EUI-64 involves inverting u bit (see explanation in Section
    // 2.5.1 of RFC2373). We need to revert that.
    bin[0] = bin[0] ^ 2;

259
260
    // Let's get the interface this packet was received on. We need it to get
    // hardware type
261
    IfacePtr iface = IfaceMgr::instance().getIface(iface_);
262
263
264
265
266
267
268
269
    uint16_t hwtype = 0; // not specified
    if (iface) {
        hwtype = iface->getHWType();
    }

    return (HWAddrPtr(new HWAddr(bin, hwtype)));
}

270
271
};
};