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