xref: /haiku/src/add-ons/kernel/network/ppp/shared/libkernelppp/KPPPLCP.cpp (revision 51978af14a173e7fae0563b562be5603bc652aeb)
1 //----------------------------------------------------------------------
2 //  This software is part of the OpenBeOS distribution and is covered
3 //  by the OpenBeOS license.
4 //
5 //  Copyright (c) 2003 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 PPPLCP::PPPLCP(PPPInterface& interface)
21 	: PPPProtocol("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 PPPLCP::~PPPLCP()
32 {
33 	while(CountOptionHandlers())
34 		delete OptionHandlerAt(0);
35 	while(CountLCPExtensions())
36 		delete LCPExtensionAt(0);
37 }
38 
39 
40 bool
41 PPPLCP::AddOptionHandler(PPPOptionHandler *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 PPPLCP::RemoveOptionHandler(PPPOptionHandler *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 PPPOptionHandler*
71 PPPLCP::OptionHandlerAt(int32 index) const
72 {
73 	PPPOptionHandler *optionHandler = fOptionHandlers.ItemAt(index);
74 
75 	if(optionHandler == fOptionHandlers.GetDefaultItem())
76 		return NULL;
77 
78 	return optionHandler;
79 }
80 
81 
82 PPPOptionHandler*
83 PPPLCP::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 	PPPOptionHandler *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 PPPLCP::AddLCPExtension(PPPLCPExtension *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 PPPLCP::RemoveLCPExtension(PPPLCPExtension *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 PPPLCPExtension*
138 PPPLCP::LCPExtensionAt(int32 index) const
139 {
140 	PPPLCPExtension *lcpExtension = fLCPExtensions.ItemAt(index);
141 
142 	if(lcpExtension == fLCPExtensions.GetDefaultItem())
143 		return NULL;
144 
145 	return lcpExtension;
146 }
147 
148 
149 PPPLCPExtension*
150 PPPLCP::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 	PPPLCPExtension *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 PPPLCP::AdditionalOverhead() const
177 {
178 	uint32 overhead = 0;
179 
180 	if(Target())
181 		overhead += Target()->Overhead();
182 
183 	return overhead;
184 }
185 
186 
187 bool
188 PPPLCP::Up()
189 {
190 	return true;
191 }
192 
193 
194 bool
195 PPPLCP::Down()
196 {
197 	return true;
198 }
199 
200 
201 status_t
202 PPPLCP::Send(struct mbuf *packet, uint16 protocolNumber)
203 {
204 	if(Target())
205 		return Target()->Send(packet, PPP_LCP_PROTOCOL);
206 	else
207 		return Interface().Send(packet, PPP_LCP_PROTOCOL);
208 }
209 
210 
211 status_t
212 PPPLCP::Receive(struct mbuf *packet, uint16 protocolNumber)
213 {
214 	if(!packet)
215 		return B_ERROR;
216 
217 	if(protocolNumber != PPP_LCP_PROTOCOL)
218 		return PPP_UNHANDLED;
219 
220 	ppp_lcp_packet *data = mtod(packet, ppp_lcp_packet*);
221 
222 	// adjust length (remove padding)
223 	int32 length = packet->m_len;
224 	if(packet->m_flags & M_PKTHDR)
225 		length = packet->m_pkthdr.len;
226 	if(length - ntohs(data->length) != 0)
227 		m_adj(packet, length);
228 
229 	struct mbuf *copy = m_gethdr(MT_DATA);
230 	if(copy) {
231 		copy->m_data += AdditionalOverhead();
232 		copy->m_len = packet->m_len;
233 		memcpy(copy->m_data, packet->m_data, copy->m_len);
234 	}
235 
236 	if(ntohs(data->length) < 4)
237 		return B_ERROR;
238 
239 	bool handled = true;
240 
241 	switch(data->code) {
242 		case PPP_CONFIGURE_REQUEST:
243 			StateMachine().RCREvent(packet);
244 		break;
245 
246 		case PPP_CONFIGURE_ACK:
247 			StateMachine().RCAEvent(packet);
248 		break;
249 
250 		case PPP_CONFIGURE_NAK:
251 		case PPP_CONFIGURE_REJECT:
252 			StateMachine().RCNEvent(packet);
253 		break;
254 
255 		case PPP_TERMINATE_REQUEST:
256 			StateMachine().RTREvent(packet);
257 		break;
258 
259 		case PPP_TERMINATE_ACK:
260 			StateMachine().RTAEvent(packet);
261 		break;
262 
263 		case PPP_CODE_REJECT:
264 			StateMachine().RXJEvent(packet);
265 		break;
266 
267 		case PPP_PROTOCOL_REJECT:
268 			StateMachine().RXJEvent(packet);
269 		break;
270 
271 		case PPP_ECHO_REQUEST:
272 		case PPP_ECHO_REPLY:
273 		case PPP_DISCARD_REQUEST:
274 			StateMachine().RXREvent(packet);
275 		break;
276 
277 		default:
278 			m_freem(packet);
279 			handled = false;
280 	}
281 
282 	packet = copy;
283 
284 	if(!packet)
285 		return handled ? B_OK : B_ERROR;
286 
287 	status_t result = B_OK;
288 
289 	// Try to find LCP extensions that can handle this code.
290 	// We must duplicate the packet in order to ask all handlers.
291 	int32 index = 0;
292 	PPPLCPExtension *lcpExtension = LCPExtensionFor(data->code, &index);
293 	for(; lcpExtension; lcpExtension = LCPExtensionFor(data->code, &(++index))) {
294 		if(!lcpExtension->IsEnabled())
295 			continue;
296 
297 		result = lcpExtension->Receive(packet, data->code);
298 
299 		// check return value and return it on error
300 		if(result == B_OK)
301 			handled = true;
302 		else if(result != PPP_UNHANDLED) {
303 			m_freem(packet);
304 			return result;
305 		}
306 	}
307 
308 	if(!handled) {
309 		StateMachine().RUCEvent(packet, PPP_LCP_PROTOCOL, PPP_CODE_REJECT);
310 		return PPP_REJECTED;
311 	}
312 
313 	m_freem(packet);
314 
315 	return result;
316 }
317 
318 
319 void
320 PPPLCP::Pulse()
321 {
322 	StateMachine().TimerEvent();
323 
324 	for(int32 index = 0; index < CountLCPExtensions(); index++)
325 		LCPExtensionAt(index)->Pulse();
326 }
327