xref: /haiku/src/add-ons/kernel/network/ppp/shared/libkernelppp/KPPPLCP.cpp (revision f2ced752a08ff5d2618826bcd3ae3976c9f3e92e)
1 //-----------------------------------------------------------------------
2 //  This software is part of the OpenBeOS distribution and is covered
3 //  by the OpenBeOS license.
4 //
5 //  Copyright (c) 2003-2004 Waldemar Kornewald, Waldemar.Kornewald@web.de
6 //-----------------------------------------------------------------------
7 
8 /*!	\class KPPPLCP
9 	\brief The LCP protocol.
10 
11 	Every PPP interface \e must have an LCP protocol. It is used for establishing
12 	and terminating connections. \n
13 	When establishing a connecition the LCP protocol determines connection-specific
14 	settings like the packet MRU. These settings are handled by the KPPPOptionHandler
15 	class. Additional LCP codes like the PPP Multilink-Protocol uses them should
16 	be implemented through the KPPPLCPExtension class. \n
17 */
18 
19 #include <KPPPInterface.h>
20 #include <KPPPDevice.h>
21 #include <KPPPLCPExtension.h>
22 #include <KPPPOptionHandler.h>
23 
24 #include <LockerHelper.h>
25 
26 #include <netinet/in.h>
27 #include <core_funcs.h>
28 #include <sys/socket.h>
29 
30 
31 //!	Creates a new LCP protocol for the given interface.
32 KPPPLCP::KPPPLCP(KPPPInterface& interface)
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.
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
59 KPPPLCP::AddOptionHandler(KPPPOptionHandler *optionHandler)
60 {
61 	if(!optionHandler || &optionHandler->Interface() != &Interface())
62 		return false;
63 
64 	LockerHelper locker(StateMachine().fLock);
65 
66 	if(Interface().Phase() != PPP_DOWN_PHASE
67 			|| OptionHandlerFor(optionHandler->Type()))
68 		return false;
69 			// a running connection may not change and there may only be
70 			// one handler per option type
71 
72 	return fOptionHandlers.AddItem(optionHandler);
73 }
74 
75 
76 /*!	\brief Removes an option handler, but does not delete it.
77 
78 	NOTE: You can only remove option handlers in \c PPP_DOWN_PHASE.
79 */
80 bool
81 KPPPLCP::RemoveOptionHandler(KPPPOptionHandler *optionHandler)
82 {
83 	LockerHelper locker(StateMachine().fLock);
84 
85 	if(Interface().Phase() != PPP_DOWN_PHASE)
86 		return false;
87 			// a running connection may not change
88 
89 	return fOptionHandlers.RemoveItem(optionHandler);
90 }
91 
92 
93 //!	Returns the option handler at the given \a index.
94 KPPPOptionHandler*
95 KPPPLCP::OptionHandlerAt(int32 index) const
96 {
97 	KPPPOptionHandler *optionHandler = fOptionHandlers.ItemAt(index);
98 
99 	if(optionHandler == fOptionHandlers.GetDefaultItem())
100 		return NULL;
101 
102 	return optionHandler;
103 }
104 
105 
106 //!	Returns the option handler that can handle options of a given \a type.
107 KPPPOptionHandler*
108 KPPPLCP::OptionHandlerFor(uint8 type, int32 *start = NULL) const
109 {
110 	// The iteration style in this method is strange C/C++.
111 	// Explanation: I use this style because it makes extending all XXXFor
112 	// methods simpler as that they look very similar, now.
113 
114 	int32 index = start ? *start : 0;
115 
116 	if(index < 0)
117 		return NULL;
118 
119 	KPPPOptionHandler *current = OptionHandlerAt(index);
120 
121 	for(; current; current = OptionHandlerAt(++index)) {
122 		if(current->Type() == type) {
123 			if(start)
124 				*start = index;
125 			return current;
126 		}
127 	}
128 
129 	return NULL;
130 }
131 
132 
133 /*!	\brief Adds a new LCP extension.
134 
135 	NOTE: You can only add LCP extensions in \c PPP_DOWN_PHASE.
136 */
137 bool
138 KPPPLCP::AddLCPExtension(KPPPLCPExtension *lcpExtension)
139 {
140 	if(!lcpExtension || &lcpExtension->Interface() != &Interface())
141 		return false;
142 
143 	LockerHelper locker(StateMachine().fLock);
144 
145 	if(Interface().Phase() != PPP_DOWN_PHASE)
146 		return false;
147 			// a running connection may not change
148 
149 	return fLCPExtensions.AddItem(lcpExtension);
150 }
151 
152 
153 /*!	\brief Removes an LCP extension, but does not delete it.
154 
155 	NOTE: You can only remove LCP extensions in \c PPP_DOWN_PHASE.
156 */
157 bool
158 KPPPLCP::RemoveLCPExtension(KPPPLCPExtension *lcpExtension)
159 {
160 	LockerHelper locker(StateMachine().fLock);
161 
162 	if(Interface().Phase() != PPP_DOWN_PHASE)
163 		return false;
164 			// a running connection may not change
165 
166 	return fLCPExtensions.RemoveItem(lcpExtension);
167 }
168 
169 
170 //!	Returns the LCP extension at the given \a index.
171 KPPPLCPExtension*
172 KPPPLCP::LCPExtensionAt(int32 index) const
173 {
174 	KPPPLCPExtension *lcpExtension = fLCPExtensions.ItemAt(index);
175 
176 	if(lcpExtension == fLCPExtensions.GetDefaultItem())
177 		return NULL;
178 
179 	return lcpExtension;
180 }
181 
182 
183 //!	Returns the LCP extension that can handle LCP packets of a given \a code.
184 KPPPLCPExtension*
185 KPPPLCP::LCPExtensionFor(uint8 code, int32 *start = NULL) const
186 {
187 	// The iteration style in this method is strange C/C++.
188 	// Explanation: I use this style because it makes extending all XXXFor
189 	// methods simpler as that they look very similar, now.
190 
191 	int32 index = start ? *start : 0;
192 
193 	if(index < 0)
194 		return NULL;
195 
196 	KPPPLCPExtension *current = LCPExtensionAt(index);
197 
198 	for(; current; current = LCPExtensionAt(++index)) {
199 		if(current->Code() == code) {
200 			if(start)
201 				*start = index;
202 			return current;
203 		}
204 	}
205 
206 	return NULL;
207 }
208 
209 
210 //!	Returns the interface, target, and device overhead.
211 uint32
212 KPPPLCP::AdditionalOverhead() const
213 {
214 	uint32 overhead = Interface().Overhead();
215 
216 	if(Target())
217 		overhead += Target()->Overhead();
218 
219 	if(Interface().Device())
220 		overhead += Interface().Device()->Overhead();
221 
222 	return overhead;
223 }
224 
225 
226 //!	Calls \c ProfileChanged() for each option handler and LCP extension.
227 void
228 KPPPLCP::ProfileChanged()
229 {
230 	KPPPLCPExtension *extension;
231 	KPPPOptionHandler *handler;
232 
233 	for(int32 index = 0; index < CountLCPExtensions(); index++) {
234 		extension = LCPExtensionAt(index);
235 		if(extension)
236 			extension->ProfileChanged();
237 	}
238 
239 	for(int32 index = 0; index < CountOptionHandlers(); index++) {
240 		handler = OptionHandlerAt(index);
241 		if(handler)
242 			handler->ProfileChanged();
243 	}
244 }
245 
246 
247 //!	Always returns \c true.
248 bool
249 KPPPLCP::Up()
250 {
251 	return true;
252 }
253 
254 
255 //!	Always returns \c true.
256 bool
257 KPPPLCP::Down()
258 {
259 	return true;
260 }
261 
262 
263 //!	Sends a packet to the target (if there is one) or to the interface.
264 status_t
265 KPPPLCP::Send(struct mbuf *packet, uint16 protocolNumber = PPP_LCP_PROTOCOL)
266 {
267 	if(Target())
268 		return Target()->Send(packet, PPP_LCP_PROTOCOL);
269 	else
270 		return Interface().Send(packet, PPP_LCP_PROTOCOL);
271 }
272 
273 
274 //!	Decodes the LCP packet and passes it to the KPPPStateMachine or an LCP extension.
275 status_t
276 KPPPLCP::Receive(struct mbuf *packet, uint16 protocolNumber)
277 {
278 	if(!packet)
279 		return B_ERROR;
280 
281 	if(protocolNumber != PPP_LCP_PROTOCOL) {
282 		dprintf("KPPPLCP::Receive(): wrong protocol number!\n");
283 		return PPP_UNHANDLED;
284 	}
285 
286 	ppp_lcp_packet *data = mtod(packet, ppp_lcp_packet*);
287 
288 	// remove padding
289 	int32 length = packet->m_len;
290 	if(packet->m_flags & M_PKTHDR)
291 		length = packet->m_pkthdr.len;
292 	length -= ntohs(data->length);
293 	if(length)
294 		m_adj(packet, -length);
295 
296 	struct mbuf *copy = m_gethdr(MT_DATA);
297 	if(copy) {
298 		copy->m_data += AdditionalOverhead();
299 		copy->m_pkthdr.len = copy->m_len = packet->m_len;
300 		memcpy(copy->m_data, packet->m_data, copy->m_len);
301 	}
302 
303 	if(ntohs(data->length) < 4)
304 		return B_ERROR;
305 
306 	bool handled = true;
307 
308 	switch(data->code) {
309 		case PPP_CONFIGURE_REQUEST:
310 			StateMachine().RCREvent(packet);
311 		break;
312 
313 		case PPP_CONFIGURE_ACK:
314 			StateMachine().RCAEvent(packet);
315 		break;
316 
317 		case PPP_CONFIGURE_NAK:
318 		case PPP_CONFIGURE_REJECT:
319 			StateMachine().RCNEvent(packet);
320 		break;
321 
322 		case PPP_TERMINATE_REQUEST:
323 			StateMachine().RTREvent(packet);
324 		break;
325 
326 		case PPP_TERMINATE_ACK:
327 			StateMachine().RTAEvent(packet);
328 		break;
329 
330 		case PPP_CODE_REJECT:
331 			StateMachine().RXJEvent(packet);
332 		break;
333 
334 		case PPP_PROTOCOL_REJECT:
335 			StateMachine().RXJEvent(packet);
336 		break;
337 
338 		case PPP_ECHO_REQUEST:
339 		case PPP_ECHO_REPLY:
340 		case PPP_DISCARD_REQUEST:
341 			StateMachine().RXREvent(packet);
342 		break;
343 
344 		default:
345 			m_freem(packet);
346 			handled = false;
347 	}
348 
349 	packet = copy;
350 
351 	if(!packet)
352 		return handled ? B_OK : B_ERROR;
353 
354 	status_t result = B_OK;
355 
356 	// Try to find LCP extensions that can handle this code.
357 	// We must duplicate the packet in order to ask all handlers.
358 	int32 index = 0;
359 	KPPPLCPExtension *lcpExtension = LCPExtensionFor(data->code, &index);
360 	for(; lcpExtension; lcpExtension = LCPExtensionFor(data->code, &(++index))) {
361 		if(!lcpExtension->IsEnabled())
362 			continue;
363 
364 		result = lcpExtension->Receive(packet, data->code);
365 
366 		// check return value and return it on error
367 		if(result == B_OK)
368 			handled = true;
369 		else if(result != PPP_UNHANDLED) {
370 			m_freem(packet);
371 			return result;
372 		}
373 	}
374 
375 	if(!handled) {
376 		StateMachine().RUCEvent(packet, PPP_LCP_PROTOCOL, PPP_CODE_REJECT);
377 		return PPP_REJECTED;
378 	}
379 
380 	m_freem(packet);
381 
382 	return result;
383 }
384 
385 
386 //!	Calls \c Pulse() for each LCP extension.
387 void
388 KPPPLCP::Pulse()
389 {
390 	StateMachine().TimerEvent();
391 
392 	for(int32 index = 0; index < CountLCPExtensions(); index++)
393 		LCPExtensionAt(index)->Pulse();
394 }
395