1 /*
2 * Copyright 2003-2007, Waldemar Kornewald <wkornew@gmx.net>
3 * Distributed under the terms of the MIT License.
4 */
5
6 /*! \class KPPPLCP
7 \brief The LCP protocol.
8
9 Every PPP interface \e must have an LCP protocol. It is used for establishing
10 and terminating connections. \n
11 When establishing a connecition the LCP protocol determines connection-specific
12 settings like the packet MRU. These settings are handled by the KPPPOptionHandler
13 class. Additional LCP codes like the PPP Multilink-Protocol uses them should
14 be implemented through the KPPPLCPExtension class. \n
15 */
16
17 #include <KPPPInterface.h>
18 #include <KPPPDevice.h>
19 #include <KPPPLCPExtension.h>
20 #include <KPPPOptionHandler.h>
21
22 #include <netinet/in.h>
23
24 #include <net_buffer.h>
25 #include <sys/socket.h>
26
27 extern net_buffer_module_info *gBufferModule;
28
29
30 //! Creates a new LCP protocol for the given interface.
KPPPLCP(KPPPInterface & interface)31 KPPPLCP::KPPPLCP(KPPPInterface& interface)
32 :
33 KPPPProtocol("LCP", PPP_ESTABLISHMENT_PHASE, PPP_LCP_PROTOCOL,
34 PPP_PROTOCOL_LEVEL, AF_UNSPEC, 0, interface, NULL, PPP_ALWAYS_ALLOWED),
35 fStateMachine(interface.StateMachine()),
36 fTarget(NULL)
37 {
38 SetUpRequested(false);
39 // the state machine does everything for us
40 }
41
42
43 //! Deletes all added option handlers and LCP extensions.
~KPPPLCP()44 KPPPLCP::~KPPPLCP()
45 {
46 while (CountOptionHandlers())
47 delete OptionHandlerAt(0);
48 while (CountLCPExtensions())
49 delete LCPExtensionAt(0);
50 }
51
52
53 /*! \brief Adds a new option handler.
54
55 NOTE: You can only add option handlers in \c PPP_DOWN_PHASE. \n
56 There may only be one handler per option type!
57 */
58 bool
AddOptionHandler(KPPPOptionHandler * optionHandler)59 KPPPLCP::AddOptionHandler(KPPPOptionHandler *optionHandler)
60 {
61 if (!optionHandler || &optionHandler->Interface() != &Interface())
62 return false;
63
64 if (Interface().Phase() != PPP_DOWN_PHASE
65 || OptionHandlerFor(optionHandler->Type()))
66 return false;
67 // a running connection may not change and there may only be
68 // one handler per option type
69
70 return fOptionHandlers.AddItem(optionHandler);
71 }
72
73
74 /*! \brief Removes an option handler, but does not delete it.
75
76 NOTE: You can only remove option handlers in \c PPP_DOWN_PHASE.
77 */
78 bool
RemoveOptionHandler(KPPPOptionHandler * optionHandler)79 KPPPLCP::RemoveOptionHandler(KPPPOptionHandler *optionHandler)
80 {
81 if (Interface().Phase() != PPP_DOWN_PHASE)
82 return false;
83 // a running connection may not change
84
85 return fOptionHandlers.RemoveItem(optionHandler);
86 }
87
88
89 //! Returns the option handler at the given \a index.
90 KPPPOptionHandler*
OptionHandlerAt(int32 index) const91 KPPPLCP::OptionHandlerAt(int32 index) const
92 {
93 dprintf("total optionhandler count %" B_PRId32 "\n", CountOptionHandlers());
94 KPPPOptionHandler *optionHandler = fOptionHandlers.ItemAt(index);
95
96 if (optionHandler == fOptionHandlers.GetDefaultItem())
97 return NULL;
98
99 return optionHandler;
100 }
101
102
103 //! Returns the option handler that can handle options of a given \a type.
104 KPPPOptionHandler*
OptionHandlerFor(uint8 type,int32 * start) const105 KPPPLCP::OptionHandlerFor(uint8 type, int32 *start) const
106 {
107 // The iteration style in this method is strange C/C++.
108 // Explanation: I use this style because it makes extending all XXXFor
109 // methods simpler as that they look very similar, now.
110
111 int32 index = start ? *start : 0;
112
113 if (index < 0)
114 return NULL;
115
116 KPPPOptionHandler *current = OptionHandlerAt(index);
117
118 for (; current; current = OptionHandlerAt(++index)) {
119 if (current->Type() == type) {
120 if (start)
121 *start = index;
122 return current;
123 }
124 }
125
126 return NULL;
127 }
128
129
130 /*! \brief Adds a new LCP extension.
131
132 NOTE: You can only add LCP extensions in \c PPP_DOWN_PHASE.
133 */
134 bool
AddLCPExtension(KPPPLCPExtension * lcpExtension)135 KPPPLCP::AddLCPExtension(KPPPLCPExtension *lcpExtension)
136 {
137 if (!lcpExtension || &lcpExtension->Interface() != &Interface())
138 return false;
139
140 if (Interface().Phase() != PPP_DOWN_PHASE)
141 return false;
142 // a running connection may not change
143
144 return fLCPExtensions.AddItem(lcpExtension);
145 }
146
147
148 /*! \brief Removes an LCP extension, but does not delete it.
149
150 NOTE: You can only remove LCP extensions in \c PPP_DOWN_PHASE.
151 */
152 bool
RemoveLCPExtension(KPPPLCPExtension * lcpExtension)153 KPPPLCP::RemoveLCPExtension(KPPPLCPExtension *lcpExtension)
154 {
155 if (Interface().Phase() != PPP_DOWN_PHASE)
156 return false;
157 // a running connection may not change
158
159 return fLCPExtensions.RemoveItem(lcpExtension);
160 }
161
162
163 //! Returns the LCP extension at the given \a index.
164 KPPPLCPExtension*
LCPExtensionAt(int32 index) const165 KPPPLCP::LCPExtensionAt(int32 index) const
166 {
167 dprintf("LCPExtension count %" B_PRId32 "\n", CountLCPExtensions());
168 KPPPLCPExtension *lcpExtension = fLCPExtensions.ItemAt(index);
169
170 if (lcpExtension == fLCPExtensions.GetDefaultItem())
171 return NULL;
172
173 return lcpExtension;
174 }
175
176
177 //! Returns the LCP extension that can handle LCP packets of a given \a code.
178 KPPPLCPExtension*
LCPExtensionFor(uint8 code,int32 * start) const179 KPPPLCP::LCPExtensionFor(uint8 code, int32 *start) const
180 {
181 // The iteration style in this method is strange C/C++.
182 // Explanation: I use this style because it makes extending all XXXFor
183 // methods simpler as that they look very similar, now.
184
185 int32 index = start ? *start : 0;
186
187 if (index < 0)
188 return NULL;
189
190 KPPPLCPExtension *current = LCPExtensionAt(index);
191
192 for (; current; current = LCPExtensionAt(++index)) {
193 if (current->Code() == code) {
194 if (start)
195 *start = index;
196 return current;
197 }
198 }
199
200 return NULL;
201 }
202
203
204 //! Always returns \c true.
205 bool
Up()206 KPPPLCP::Up()
207 {
208 return true;
209 }
210
211
212 //! Always returns \c true.
213 bool
Down()214 KPPPLCP::Down()
215 {
216 return true;
217 }
218
219
220 //! Sends a packet to the target (if there is one) or to the interface.
221 status_t
Send(net_buffer * packet,uint16 protocolNumber)222 KPPPLCP::Send(net_buffer *packet, uint16 protocolNumber)
223 {
224 if (Target())
225 return Target()->Send(packet, PPP_LCP_PROTOCOL);
226 else
227 return Interface().Send(packet, PPP_LCP_PROTOCOL);
228 }
229
230
231 //! Decodes the LCP packet and passes it to the KPPPStateMachine or an LCP extension.
232 status_t
Receive(net_buffer * packet,uint16 protocolNumber)233 KPPPLCP::Receive(net_buffer *packet, uint16 protocolNumber)
234 {
235 if (!packet)
236 return B_ERROR;
237
238 if (protocolNumber != PPP_LCP_PROTOCOL) {
239 ERROR("KPPPLCP::Receive(): wrong protocol number!\n");
240 return PPP_UNHANDLED;
241 }
242
243 NetBufferHeaderReader<ppp_lcp_packet> bufferHeader(packet);
244 if (bufferHeader.Status() < B_OK)
245 return bufferHeader.Status();
246
247 ppp_lcp_packet &data = bufferHeader.Data();
248
249 // remove padding
250
251 net_buffer *copy = gBufferModule->duplicate(packet);
252
253 if (ntohs(data.length) < 4)
254 return B_ERROR;
255
256 bool handled = true;
257
258 switch (data.code) {
259 case PPP_CONFIGURE_REQUEST:
260 StateMachine().RCREvent(packet);
261 break;
262
263 case PPP_CONFIGURE_ACK:
264 StateMachine().RCAEvent(packet);
265 break;
266
267 case PPP_CONFIGURE_NAK:
268 case PPP_CONFIGURE_REJECT:
269 StateMachine().RCNEvent(packet);
270 break;
271
272 case PPP_TERMINATE_REQUEST:
273 StateMachine().RTREvent(packet);
274 break;
275
276 case PPP_TERMINATE_ACK:
277 StateMachine().RTAEvent(packet);
278 break;
279
280 case PPP_CODE_REJECT:
281 StateMachine().RXJEvent(packet);
282 break;
283
284 case PPP_PROTOCOL_REJECT:
285 StateMachine().RXJEvent(packet);
286 break;
287
288 case PPP_ECHO_REQUEST:
289 case PPP_ECHO_REPLY:
290 case PPP_DISCARD_REQUEST:
291 StateMachine().RXREvent(packet);
292 break;
293
294 default:
295 // gBufferModule->free(packet);
296 handled = false;
297 }
298
299 packet = copy;
300
301 if (!packet)
302 return handled ? B_OK : B_ERROR;
303
304 status_t result = B_OK;
305
306 // Try to find LCP extensions that can handle this code.
307 // We must duplicate the packet in order to ask all handlers.
308 int32 index = 0;
309 KPPPLCPExtension *lcpExtension = LCPExtensionFor(data.code, &index);
310 for (; lcpExtension; lcpExtension = LCPExtensionFor(data.code, &(++index))) {
311 if (!lcpExtension->IsEnabled())
312 continue;
313
314 result = lcpExtension->Receive(packet, data.code);
315
316 // check return value and return it on error
317 if (result == B_OK)
318 handled = true;
319 else if (result != PPP_UNHANDLED) {
320 gBufferModule->free(packet);
321 return result;
322 }
323 }
324
325 if (!handled) {
326 StateMachine().RUCEvent(packet, PPP_LCP_PROTOCOL, PPP_CODE_REJECT);
327 return PPP_REJECTED;
328 }
329
330 gBufferModule->free(packet);
331
332 return result;
333 }
334
335
336 //! Calls \c Pulse() for each LCP extension.
337 void
Pulse()338 KPPPLCP::Pulse()
339 {
340 StateMachine().TimerEvent();
341
342 for (int32 index = 0; index < CountLCPExtensions(); index++)
343 LCPExtensionAt(index)->Pulse();
344 }
345