/
ClntMsgSolicit.cpp
311 lines (275 loc) · 9.1 KB
/
ClntMsgSolicit.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
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
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
/*
* Dibbler - a portable DHCPv6
*
* authors: Tomasz Mrugalski <thomson@klub.com.pl>
* Marek Senderski <msend@o2.pl>
* chamges: Krzysztof Wnuk <keczi@poczta.onet.pl>
* released under GNU GPL v2 only licence
*
* $Id: ClntMsgSolicit.cpp,v 1.27 2009-03-24 23:17:17 thomson Exp $
*/
#include "SmartPtr.h"
#include "Msg.h"
#include "ClntIfaceMgr.h"
#include "ClntCfgIface.h"
#include "ClntCfgIA.h"
#include "ClntMsg.h"
#include "ClntMsgSolicit.h"
#include "ClntMsgAdvertise.h"
#include "ClntOptIA_NA.h"
#include "ClntOptTA.h"
#include "ClntOptIA_PD.h"
#include "OptDUID.h"
#include "ClntOptOptionRequest.h"
#include "ClntOptElapsed.h"
#include "ClntOptPreference.h"
#include "OptEmpty.h"
#include "ClntOptStatusCode.h"
#include <cmath>
#include "Logger.h"
TClntMsgSolicit::TClntMsgSolicit(int iface, SPtr<TIPv6Addr> addr,
List(TClntCfgIA) iaLst,
SPtr<TClntCfgTA> ta,
List(TClntCfgPD) pdLst,
bool rapid, bool remoteAutoconf)
:TClntMsg(iface,addr,SOLICIT_MSG)
{
IRT=SOL_TIMEOUT;
MRT=SOL_MAX_RT;
MRC=0; //these both below mean there is no ending condition and transactions
MRD=0; //lasts till receiving answer
RT=0;
// ClientIdentifier option
appendClientID();
SPtr<TAddrIA> addrIA;
// all IAs are provided by ::checkSolicit()
SPtr<TClntCfgIA> ia;
iaLst.first();
while (ia = iaLst.get()) {
SPtr<TClntOptIA_NA> iaOpt;
iaOpt = new TClntOptIA_NA(ia, this);
Options.push_back( (Ptr*)iaOpt );
if (!remoteAutoconf)
ia->setState(STATE_INPROCESS);
addrIA = ClntAddrMgr().getIA(ia->getIAID());
if (addrIA)
addrIA->setState(STATE_INPROCESS);
else
Log(Error) << "AddrMgr does not have IA with IAID=" << ia->getIAID() << LogEnd;
}
// TA is provided by ::checkSolicit()
if (ta) {
SPtr<TClntOptTA> taOpt = new TClntOptTA(ta->getIAID(), this);
Options.push_back( (Ptr*) taOpt);
if (!remoteAutoconf)
ta->setState(STATE_INPROCESS);
addrIA = ClntAddrMgr().getTA(ta->getIAID());
if (addrIA)
addrIA->setState(STATE_INPROCESS);
else
Log(Error) << "AddrMgr does not have TA with IAID=" << ia->getIAID() << LogEnd;
}
// all PDs are provided by ::checkSolicit()
SPtr<TClntCfgPD> pd;
pdLst.first();
while ( pd = pdLst.get() ) {
SPtr<TClntOptIA_PD> pdOpt = new TClntOptIA_PD(pd, this);
Options.push_back( (Ptr*)pdOpt );
if (!remoteAutoconf)
pd->setState(STATE_INPROCESS);
addrIA = ClntAddrMgr().getPD(pd->getIAID());
if (addrIA)
addrIA->setState(STATE_INPROCESS);
else
Log(Error) << "AddrMgr does not have PD with IAID=" << pd->getIAID() << LogEnd;
}
if(rapid)
Options.push_back(new TOptEmpty(OPTION_RAPID_COMMIT, this));
// append and switch to INPROCESS state
if (!remoteAutoconf)
appendTAOptions(true);
// append options specified in the config file
if (!remoteAutoconf)
appendRequestedOptions();
IsDone = false;
send();
}
void TClntMsgSolicit::answer(SPtr<TClntMsg> msg)
{
if (shallRejectAnswer(msg)) {
Log(Info) << "Server message was rejected." << LogEnd;
return;
}
switch (msg->getType()) {
case ADVERTISE_MSG:
{
if (this->getOption(OPTION_RAPID_COMMIT)) {
Log(Info) << "Server responded with ADVERTISE instead of REPLY, probably does not support"
" RAPID-COMMIT." << LogEnd;
}
ClntTransMgr().addAdvertise((Ptr*)msg);
SPtr<TOptInteger> prefOpt = (Ptr*) msg->getOption(OPTION_PREFERENCE);
if (prefOpt && (prefOpt->getValue() == 255) )
{
Log(Info) << "ADVERTISE message with prefrence set to 255 received, so wait time for"
" other possible ADVERTISE messages is skipped." << LogEnd;
ClntTransMgr().sendRequest(Options,Iface);
IsDone = true;
return;
}
if (this->RC > 1)
{
ClntTransMgr().sendRequest(Options,Iface);
IsDone = true;
return;
}
break;
}
case REPLY_MSG:
{
if (!this->getOption(OPTION_RAPID_COMMIT)) {
Log(Warning) << "REPLY received, but SOLICIT was sent without RAPID_COMMIT. Ignoring."
<< LogEnd;
return;
}
if (!msg->getOption(OPTION_RAPID_COMMIT)) {
Log(Warning) << "REPLY as answer for SOLICIT received without RAPID_COMMIT. Ignoring."
<< LogEnd;
return;
}
TClntMsg::answer(msg);
break;
}
default:
Log(Warning) << "Invalid message type (" << msg->getType()
<< ") received as answer for SOLICIT message." << LogEnd;
return;
}
}
/// @brief check if received message should be accepted.
///
/// The following conditions are checked:
/// - is server on the black-list?
/// - are all requested options present?
/// - is there requested IA option?
/// - is there requested TA option?
///
/// @param msg server's REPLY
///
/// @return true if REPLY is rejected
bool TClntMsgSolicit::shallRejectAnswer(SPtr<TClntMsg> msg)
{
bool somethingAssigned = false;
// this == solicit or request
// msg == reply
SPtr<TOptDUID> srvDUID = (Ptr*) msg->getOption(OPTION_SERVERID);
if (!srvDUID) {
Log(Notice) << "No server identifier provided. Message ignored." << LogEnd;
return true;
}
//is this server rejected?
SPtr<TClntCfgIface> iface = ClntCfgMgr().getIface(this->Iface);
if (!iface) {
Log(Error) << "Unable to find iface=" << this->Iface << "." << LogEnd;
return true;
}
if (iface->isServerRejected(msg->getAddr(), srvDUID->getDUID())) {
Log(Notice) << "Server was rejected (duid=" << srvDUID->getDUID() << ")." << LogEnd;
return true;
}
// have we asked for IA?
bool iaOk = true;
if (this->getOption(OPTION_IA_NA))
{
///@todo Check if proper IAIDs are returned, also if all IA were answered (if requested several IAs were requested)
///@todo Check all IA_NAs, not just first one
SPtr<TClntOptIA_NA> ia = (Ptr*)msg->getOption(OPTION_IA_NA);
if (!ia) {
Log(Notice) << "IA_NA option requested, but not present in this message. Ignored." << LogEnd;
iaOk = false;
}
else if (!ia->getOption(OPTION_IAADDR)) {
Log(Notice) << "IA_NA option returned, but without any addresses. Ignored." << LogEnd;
iaOk = false;
}
SPtr<TClntOptStatusCode> st = (Ptr*)ia->getOption(OPTION_STATUS_CODE);
if (st && st->getCode()!= STATUSCODE_SUCCESS) {
Log(Notice) << "IA_NA has status code!=SUCCESS: " << st->getCode()
<< "(" << st->getText() << "). Ignored." << LogEnd;
iaOk = false;
}
if (iaOk)
somethingAssigned = true;
}
// have we asked for TA?
bool taOk = true;
if (this->getOption(OPTION_IA_TA)) {
SPtr<TClntOptTA> ta = (Ptr*)msg->getOption(OPTION_IA_TA);
if (!ta) {
Log(Notice) << "TA option requested, but not present in this message. Ignored." << LogEnd;
taOk = false;
} else {
if (!ta->getOption(OPTION_IAADDR)) {
Log(Notice) << "TA option received, but without IAADDR" << LogEnd;
taOk = false;
}
SPtr<TClntOptStatusCode> st = (Ptr*)ta->getOption(OPTION_STATUS_CODE);
if (st && st->getCode()!= STATUSCODE_SUCCESS) {
Log(Notice) << "IA_TA has status code!=SUCCESS: " << st->getCode()
<< "(" << st->getText() << "). Ignored." << LogEnd;
taOk = false;
}
}
if (taOk)
somethingAssigned = true;
}
// have we asked for PD?
bool pdOk = true;
if (getOption(OPTION_IA_PD)) {
SPtr<TClntOptIA_PD> pd = (Ptr*) msg->getOption(OPTION_IA_PD);
if (!pd) {
Log(Notice) << "PD option requested, but not returned in this message. Ignored." << LogEnd;
pdOk = false;
}
if (!pd->getOption(OPTION_IAPREFIX)) {
Log(Notice) << "Received PD without any prefixes." << LogEnd;
pdOk = false;
}
SPtr<TClntOptStatusCode> st = (Ptr*)pd->getOption(OPTION_STATUS_CODE);
if (st && st->getCode()!= STATUSCODE_SUCCESS) {
Log(Notice) << "IA_NA has status code!=SUCCESS: " << st->getCode()
<< "(" << st->getText() << "). Ignored." << LogEnd;
pdOk = false;
}
if (pdOk)
somethingAssigned = true;
}
if (!somethingAssigned)
return true; // this advertise does not offers us anything
if (!ClntCfgMgr().insistMode())
return false; // accept this advertise
// insist-mode enabled. We MUST get everything we wanted or we reject this answer
if (iaOk && taOk && pdOk)
return false;
else
return true;
}
void TClntMsgSolicit::doDuties()
{
if ( ClntTransMgr().getAdvertiseLstCount() ) {
// there is a timeout, but we have already answers and all is ok
ClntTransMgr().sendRequest(Options, Iface);
IsDone = true;
return;
}
send();
}
bool TClntMsgSolicit::check()
{
return false;
}
string TClntMsgSolicit::getName() {
return "SOLICIT";
}
TClntMsgSolicit::~TClntMsgSolicit() {
}