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
27extern net_buffer_module_info *gBufferModule;
28
29
30//!	Creates a new LCP protocol for the given interface.
31KPPPLCP::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.
44KPPPLCP::~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*/
58bool
59KPPPLCP::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*/
78bool
79KPPPLCP::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.
90KPPPOptionHandler*
91KPPPLCP::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.
104KPPPOptionHandler*
105KPPPLCP::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*/
134bool
135KPPPLCP::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*/
152bool
153KPPPLCP::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.
164KPPPLCPExtension*
165KPPPLCP::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.
178KPPPLCPExtension*
179KPPPLCP::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.
205bool
206KPPPLCP::Up()
207{
208	return true;
209}
210
211
212//!	Always returns \c true.
213bool
214KPPPLCP::Down()
215{
216	return true;
217}
218
219
220//!	Sends a packet to the target (if there is one) or to the interface.
221status_t
222KPPPLCP::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.
232status_t
233KPPPLCP::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.
337void
338KPPPLCP::Pulse()
339{
340	StateMachine().TimerEvent();
341
342	for (int32 index = 0; index < CountLCPExtensions(); index++)
343		LCPExtensionAt(index)->Pulse();
344}
345