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