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