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