xref: /haiku/src/add-ons/kernel/network/ppp/shared/libkernelppp/KPPPLCP.cpp (revision 9eb55bc1d104b8fda80898f8b25c94d8000c8255)
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, PPP_PROTOCOL_LEVEL,
22 		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 bool
191 KPPPLCP::Up()
192 {
193 	return true;
194 }
195 
196 
197 bool
198 KPPPLCP::Down()
199 {
200 	return true;
201 }
202 
203 
204 status_t
205 KPPPLCP::Send(struct mbuf *packet, uint16 protocolNumber = PPP_LCP_PROTOCOL)
206 {
207 	if(Target())
208 		return Target()->Send(packet, PPP_LCP_PROTOCOL);
209 	else
210 		return Interface().Send(packet, PPP_LCP_PROTOCOL);
211 }
212 
213 
214 status_t
215 KPPPLCP::Receive(struct mbuf *packet, uint16 protocolNumber)
216 {
217 	if(!packet)
218 		return B_ERROR;
219 
220 	if(protocolNumber != PPP_LCP_PROTOCOL) {
221 		dprintf("KPPPLCP::Receive(): wrong protocol number!\n");
222 		return PPP_UNHANDLED;
223 	}
224 
225 	ppp_lcp_packet *data = mtod(packet, ppp_lcp_packet*);
226 
227 	// remove padding
228 	int32 length = packet->m_len;
229 	if(packet->m_flags & M_PKTHDR)
230 		length = packet->m_pkthdr.len;
231 	length -= ntohs(data->length);
232 	if(length)
233 		m_adj(packet, -length);
234 
235 	struct mbuf *copy = m_gethdr(MT_DATA);
236 	if(copy) {
237 		copy->m_data += AdditionalOverhead();
238 		copy->m_pkthdr.len = copy->m_len = packet->m_len;
239 		memcpy(copy->m_data, packet->m_data, copy->m_len);
240 	}
241 
242 	if(ntohs(data->length) < 4)
243 		return B_ERROR;
244 
245 	bool handled = true;
246 
247 	switch(data->code) {
248 		case PPP_CONFIGURE_REQUEST:
249 			StateMachine().RCREvent(packet);
250 		break;
251 
252 		case PPP_CONFIGURE_ACK:
253 			StateMachine().RCAEvent(packet);
254 		break;
255 
256 		case PPP_CONFIGURE_NAK:
257 		case PPP_CONFIGURE_REJECT:
258 			StateMachine().RCNEvent(packet);
259 		break;
260 
261 		case PPP_TERMINATE_REQUEST:
262 			StateMachine().RTREvent(packet);
263 		break;
264 
265 		case PPP_TERMINATE_ACK:
266 			StateMachine().RTAEvent(packet);
267 		break;
268 
269 		case PPP_CODE_REJECT:
270 			StateMachine().RXJEvent(packet);
271 		break;
272 
273 		case PPP_PROTOCOL_REJECT:
274 			StateMachine().RXJEvent(packet);
275 		break;
276 
277 		case PPP_ECHO_REQUEST:
278 		case PPP_ECHO_REPLY:
279 		case PPP_DISCARD_REQUEST:
280 			StateMachine().RXREvent(packet);
281 		break;
282 
283 		default:
284 			m_freem(packet);
285 			handled = false;
286 	}
287 
288 	packet = copy;
289 
290 	if(!packet)
291 		return handled ? B_OK : B_ERROR;
292 
293 	status_t result = B_OK;
294 
295 	// Try to find LCP extensions that can handle this code.
296 	// We must duplicate the packet in order to ask all handlers.
297 	int32 index = 0;
298 	KPPPLCPExtension *lcpExtension = LCPExtensionFor(data->code, &index);
299 	for(; lcpExtension; lcpExtension = LCPExtensionFor(data->code, &(++index))) {
300 		if(!lcpExtension->IsEnabled())
301 			continue;
302 
303 		result = lcpExtension->Receive(packet, data->code);
304 
305 		// check return value and return it on error
306 		if(result == B_OK)
307 			handled = true;
308 		else if(result != PPP_UNHANDLED) {
309 			m_freem(packet);
310 			return result;
311 		}
312 	}
313 
314 	if(!handled) {
315 		StateMachine().RUCEvent(packet, PPP_LCP_PROTOCOL, PPP_CODE_REJECT);
316 		return PPP_REJECTED;
317 	}
318 
319 	m_freem(packet);
320 
321 	return result;
322 }
323 
324 
325 void
326 KPPPLCP::Pulse()
327 {
328 	StateMachine().TimerEvent();
329 
330 	for(int32 index = 0; index < CountLCPExtensions(); index++)
331 		LCPExtensionAt(index)->Pulse();
332 }
333