xref: /haiku/src/add-ons/kernel/network/ppp/shared/libkernelppp/KPPPLCP.cpp (revision 1e60bdeab63fa7a57bc9a55b032052e95a18bd2c)
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 
24 #include <net_buffer.h>
25 #include <sys/socket.h>
26 
27 extern net_buffer_module_info *gBufferModule;
28 
29 
30 //!	Creates a new LCP protocol for the given interface.
31 KPPPLCP::KPPPLCP(KPPPInterface& interface)
32 	:
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 	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 	if (Interface().Phase() != PPP_DOWN_PHASE)
82 		return false;
83 			// a running connection may not change
84 
85 	return fOptionHandlers.RemoveItem(optionHandler);
86 }
87 
88 
89 //!	Returns the option handler at the given \a index.
90 KPPPOptionHandler*
91 KPPPLCP::OptionHandlerAt(int32 index) const
92 {
93 	dprintf("total optionhandler count %" B_PRId32 "\n", CountOptionHandlers());
94 	KPPPOptionHandler *optionHandler = fOptionHandlers.ItemAt(index);
95 
96 	if (optionHandler == fOptionHandlers.GetDefaultItem())
97 		return NULL;
98 
99 	return optionHandler;
100 }
101 
102 
103 //!	Returns the option handler that can handle options of a given \a type.
104 KPPPOptionHandler*
105 KPPPLCP::OptionHandlerFor(uint8 type, int32 *start) const
106 {
107 	// The iteration style in this method is strange C/C++.
108 	// Explanation: I use this style because it makes extending all XXXFor
109 	// methods simpler as that they look very similar, now.
110 
111 	int32 index = start ? *start : 0;
112 
113 	if (index < 0)
114 		return NULL;
115 
116 	KPPPOptionHandler *current = OptionHandlerAt(index);
117 
118 	for (; current; current = OptionHandlerAt(++index)) {
119 		if (current->Type() == type) {
120 			if (start)
121 				*start = index;
122 			return current;
123 		}
124 	}
125 
126 	return NULL;
127 }
128 
129 
130 /*!	\brief Adds a new LCP extension.
131 
132 	NOTE: You can only add LCP extensions in \c PPP_DOWN_PHASE.
133 */
134 bool
135 KPPPLCP::AddLCPExtension(KPPPLCPExtension *lcpExtension)
136 {
137 	if (!lcpExtension || &lcpExtension->Interface() != &Interface())
138 		return false;
139 
140 	if (Interface().Phase() != PPP_DOWN_PHASE)
141 		return false;
142 			// a running connection may not change
143 
144 	return fLCPExtensions.AddItem(lcpExtension);
145 }
146 
147 
148 /*!	\brief Removes an LCP extension, but does not delete it.
149 
150 	NOTE: You can only remove LCP extensions in \c PPP_DOWN_PHASE.
151 */
152 bool
153 KPPPLCP::RemoveLCPExtension(KPPPLCPExtension *lcpExtension)
154 {
155 	if (Interface().Phase() != PPP_DOWN_PHASE)
156 		return false;
157 			// a running connection may not change
158 
159 	return fLCPExtensions.RemoveItem(lcpExtension);
160 }
161 
162 
163 //!	Returns the LCP extension at the given \a index.
164 KPPPLCPExtension*
165 KPPPLCP::LCPExtensionAt(int32 index) const
166 {
167 	dprintf("LCPExtension count %" B_PRId32 "\n", CountLCPExtensions());
168 	KPPPLCPExtension *lcpExtension = fLCPExtensions.ItemAt(index);
169 
170 	if (lcpExtension == fLCPExtensions.GetDefaultItem())
171 		return NULL;
172 
173 	return lcpExtension;
174 }
175 
176 
177 //!	Returns the LCP extension that can handle LCP packets of a given \a code.
178 KPPPLCPExtension*
179 KPPPLCP::LCPExtensionFor(uint8 code, int32 *start) const
180 {
181 	// The iteration style in this method is strange C/C++.
182 	// Explanation: I use this style because it makes extending all XXXFor
183 	// methods simpler as that they look very similar, now.
184 
185 	int32 index = start ? *start : 0;
186 
187 	if (index < 0)
188 		return NULL;
189 
190 	KPPPLCPExtension *current = LCPExtensionAt(index);
191 
192 	for (; current; current = LCPExtensionAt(++index)) {
193 		if (current->Code() == code) {
194 			if (start)
195 				*start = index;
196 			return current;
197 		}
198 	}
199 
200 	return NULL;
201 }
202 
203 
204 //!	Always returns \c true.
205 bool
206 KPPPLCP::Up()
207 {
208 	return true;
209 }
210 
211 
212 //!	Always returns \c true.
213 bool
214 KPPPLCP::Down()
215 {
216 	return true;
217 }
218 
219 
220 //!	Sends a packet to the target (if there is one) or to the interface.
221 status_t
222 KPPPLCP::Send(net_buffer *packet, uint16 protocolNumber)
223 {
224 	if (Target())
225 		return Target()->Send(packet, PPP_LCP_PROTOCOL);
226 	else
227 		return Interface().Send(packet, PPP_LCP_PROTOCOL);
228 }
229 
230 
231 //!	Decodes the LCP packet and passes it to the KPPPStateMachine or an LCP extension.
232 status_t
233 KPPPLCP::Receive(net_buffer *packet, uint16 protocolNumber)
234 {
235 	if (!packet)
236 		return B_ERROR;
237 
238 	if (protocolNumber != PPP_LCP_PROTOCOL) {
239 		ERROR("KPPPLCP::Receive(): wrong protocol number!\n");
240 		return PPP_UNHANDLED;
241 	}
242 
243 	NetBufferHeaderReader<ppp_lcp_packet> bufferHeader(packet);
244 	if (bufferHeader.Status() < B_OK)
245 		return bufferHeader.Status();
246 
247 	ppp_lcp_packet &data = bufferHeader.Data();
248 
249 	// remove padding
250 
251 	net_buffer *copy = gBufferModule->duplicate(packet);
252 
253 	if (ntohs(data.length) < 4)
254 		return B_ERROR;
255 
256 	bool handled = true;
257 
258 	switch (data.code) {
259 		case PPP_CONFIGURE_REQUEST:
260 			StateMachine().RCREvent(packet);
261 			break;
262 
263 		case PPP_CONFIGURE_ACK:
264 			StateMachine().RCAEvent(packet);
265 			break;
266 
267 		case PPP_CONFIGURE_NAK:
268 		case PPP_CONFIGURE_REJECT:
269 			StateMachine().RCNEvent(packet);
270 			break;
271 
272 		case PPP_TERMINATE_REQUEST:
273 			StateMachine().RTREvent(packet);
274 			break;
275 
276 		case PPP_TERMINATE_ACK:
277 			StateMachine().RTAEvent(packet);
278 			break;
279 
280 		case PPP_CODE_REJECT:
281 			StateMachine().RXJEvent(packet);
282 			break;
283 
284 		case PPP_PROTOCOL_REJECT:
285 			StateMachine().RXJEvent(packet);
286 			break;
287 
288 		case PPP_ECHO_REQUEST:
289 		case PPP_ECHO_REPLY:
290 		case PPP_DISCARD_REQUEST:
291 			StateMachine().RXREvent(packet);
292 			break;
293 
294 		default:
295 			// gBufferModule->free(packet);
296 			handled = false;
297 	}
298 
299 	packet = copy;
300 
301 	if (!packet)
302 		return handled ? B_OK : B_ERROR;
303 
304 	status_t result = B_OK;
305 
306 	// Try to find LCP extensions that can handle this code.
307 	// We must duplicate the packet in order to ask all handlers.
308 	int32 index = 0;
309 	KPPPLCPExtension *lcpExtension = LCPExtensionFor(data.code, &index);
310 	for (; lcpExtension; lcpExtension = LCPExtensionFor(data.code, &(++index))) {
311 		if (!lcpExtension->IsEnabled())
312 			continue;
313 
314 		result = lcpExtension->Receive(packet, data.code);
315 
316 		// check return value and return it on error
317 		if (result == B_OK)
318 			handled = true;
319 		else if (result != PPP_UNHANDLED) {
320 			gBufferModule->free(packet);
321 			return result;
322 		}
323 	}
324 
325 	if (!handled) {
326 		StateMachine().RUCEvent(packet, PPP_LCP_PROTOCOL, PPP_CODE_REJECT);
327 		return PPP_REJECTED;
328 	}
329 
330 	gBufferModule->free(packet);
331 
332 	return result;
333 }
334 
335 
336 //!	Calls \c Pulse() for each LCP extension.
337 void
338 KPPPLCP::Pulse()
339 {
340 	StateMachine().TimerEvent();
341 
342 	for (int32 index = 0; index < CountLCPExtensions(); index++)
343 		LCPExtensionAt(index)->Pulse();
344 }
345