xref: /haiku/src/add-ons/kernel/network/ppp/shared/libkernelppp/KPPPLCP.cpp (revision d3d8b26997fac34a84981e6d2b649521de2cc45a)
1 /*
2  * Copyright 2003-2005, 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 <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) 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) 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 //!	Always returns \c true.
225 bool
226 KPPPLCP::Up()
227 {
228 	return true;
229 }
230 
231 
232 //!	Always returns \c true.
233 bool
234 KPPPLCP::Down()
235 {
236 	return true;
237 }
238 
239 
240 //!	Sends a packet to the target (if there is one) or to the interface.
241 status_t
242 KPPPLCP::Send(struct mbuf *packet, uint16 protocolNumber)
243 {
244 	if(Target())
245 		return Target()->Send(packet, PPP_LCP_PROTOCOL);
246 	else
247 		return Interface().Send(packet, PPP_LCP_PROTOCOL);
248 }
249 
250 
251 //!	Decodes the LCP packet and passes it to the KPPPStateMachine or an LCP extension.
252 status_t
253 KPPPLCP::Receive(struct mbuf *packet, uint16 protocolNumber)
254 {
255 	if(!packet)
256 		return B_ERROR;
257 
258 	if(protocolNumber != PPP_LCP_PROTOCOL) {
259 		ERROR("KPPPLCP::Receive(): wrong protocol number!\n");
260 		return PPP_UNHANDLED;
261 	}
262 
263 	ppp_lcp_packet *data = mtod(packet, ppp_lcp_packet*);
264 
265 	// remove padding
266 	int32 length = packet->m_len;
267 	if(packet->m_flags & M_PKTHDR)
268 		length = packet->m_pkthdr.len;
269 	length -= ntohs(data->length);
270 	if(length)
271 		m_adj(packet, -length);
272 
273 	struct mbuf *copy = m_gethdr(MT_DATA);
274 	if(copy) {
275 		copy->m_data += AdditionalOverhead();
276 		copy->m_pkthdr.len = copy->m_len = packet->m_len;
277 		memcpy(copy->m_data, packet->m_data, copy->m_len);
278 	}
279 
280 	if(ntohs(data->length) < 4)
281 		return B_ERROR;
282 
283 	bool handled = true;
284 
285 	switch(data->code) {
286 		case PPP_CONFIGURE_REQUEST:
287 			StateMachine().RCREvent(packet);
288 		break;
289 
290 		case PPP_CONFIGURE_ACK:
291 			StateMachine().RCAEvent(packet);
292 		break;
293 
294 		case PPP_CONFIGURE_NAK:
295 		case PPP_CONFIGURE_REJECT:
296 			StateMachine().RCNEvent(packet);
297 		break;
298 
299 		case PPP_TERMINATE_REQUEST:
300 			StateMachine().RTREvent(packet);
301 		break;
302 
303 		case PPP_TERMINATE_ACK:
304 			StateMachine().RTAEvent(packet);
305 		break;
306 
307 		case PPP_CODE_REJECT:
308 			StateMachine().RXJEvent(packet);
309 		break;
310 
311 		case PPP_PROTOCOL_REJECT:
312 			StateMachine().RXJEvent(packet);
313 		break;
314 
315 		case PPP_ECHO_REQUEST:
316 		case PPP_ECHO_REPLY:
317 		case PPP_DISCARD_REQUEST:
318 			StateMachine().RXREvent(packet);
319 		break;
320 
321 		default:
322 			m_freem(packet);
323 			handled = false;
324 	}
325 
326 	packet = copy;
327 
328 	if(!packet)
329 		return handled ? B_OK : B_ERROR;
330 
331 	status_t result = B_OK;
332 
333 	// Try to find LCP extensions that can handle this code.
334 	// We must duplicate the packet in order to ask all handlers.
335 	int32 index = 0;
336 	KPPPLCPExtension *lcpExtension = LCPExtensionFor(data->code, &index);
337 	for(; lcpExtension; lcpExtension = LCPExtensionFor(data->code, &(++index))) {
338 		if(!lcpExtension->IsEnabled())
339 			continue;
340 
341 		result = lcpExtension->Receive(packet, data->code);
342 
343 		// check return value and return it on error
344 		if(result == B_OK)
345 			handled = true;
346 		else if(result != PPP_UNHANDLED) {
347 			m_freem(packet);
348 			return result;
349 		}
350 	}
351 
352 	if(!handled) {
353 		StateMachine().RUCEvent(packet, PPP_LCP_PROTOCOL, PPP_CODE_REJECT);
354 		return PPP_REJECTED;
355 	}
356 
357 	m_freem(packet);
358 
359 	return result;
360 }
361 
362 
363 //!	Calls \c Pulse() for each LCP extension.
364 void
365 KPPPLCP::Pulse()
366 {
367 	StateMachine().TimerEvent();
368 
369 	for(int32 index = 0; index < CountLCPExtensions(); index++)
370 		LCPExtensionAt(index)->Pulse();
371 }
372