xref: /haiku/src/add-ons/kernel/network/ppp/pppoe/PPPoEDevice.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 "PPPoEDevice.h"
9 #include "DiscoveryPacket.h"
10 
11 #include <core_funcs.h>
12 #include <cstdlib>
13 #include <kernel_cpp.h>
14 
15 // from libkernelppp
16 #include <settings_tools.h>
17 #include <LockerHelper.h>
18 
19 #ifdef _KERNEL_MODE
20 	#define spawn_thread spawn_kernel_thread
21 	#define printf dprintf
22 #endif
23 
24 
25 PPPoEDevice::PPPoEDevice(PPPInterface& interface, driver_parameter *settings)
26 	: PPPDevice("PPPoE", interface, settings),
27 	fEthernetIfnet(NULL),
28 	fSessionID(0),
29 	fHostUniq(NewHostUniq()),
30 	fACName(NULL),
31 	fServiceName(NULL),
32 	fNextTimeout(0),
33 	fState(INITIAL)
34 {
35 #if DEBUG
36 	printf("PPPoEDevice: Constructor\n");
37 	if(!settings || !settings->parameters)
38 		printf("PPPoEDevice::ctor: No settings!\n");
39 	else if(settings->parameter_count > 0 && settings->parameters[0].value_count > 0)
40 		printf("PPPoEDevice::ctor: Value0: %s\n", settings->parameters[0].values[0]);
41 	else
42 		printf("PPPoEDevice::ctor: No values!\n");
43 #endif
44 
45 	memset(fPeer, 0xFF, sizeof(fPeer));
46 	SetMTU(1494);
47 		// MTU size does not contain PPP header
48 
49 	// find ethernet device
50 	const char *interfaceName = get_parameter_value(PPPoE_INTERFACE_KEY, settings);
51 	if(!interfaceName)
52 		return;
53 #if DEBUG
54 	printf("PPPoEDevice::ctor: interfaceName: %s\n", interfaceName);
55 #endif
56 
57 	ifnet *current = get_interfaces();
58 	for(; current; current = current->if_next) {
59 		if(current->if_name && !strcmp(current->if_name, interfaceName)) {
60 #if DEBUG
61 			printf("PPPoEDevice::ctor: found ethernet interface\n");
62 #endif
63 			fEthernetIfnet = current;
64 			break;
65 		}
66 	}
67 
68 #if DEBUG
69 	if(!fEthernetIfnet)
70 		printf("PPPoEDevice::ctor: could not find ethernet interface\n");
71 #endif
72 }
73 
74 
75 PPPoEDevice::~PPPoEDevice()
76 {
77 #if DEBUG
78 	printf("PPPoEDevice: Destructor\n");
79 #endif
80 
81 	free(fACName);
82 	free(fServiceName);
83 }
84 
85 
86 status_t
87 PPPoEDevice::InitCheck() const
88 {
89 	return EthernetIfnet() && EthernetIfnet()->output
90 		&& PPPDevice::InitCheck() == B_OK ? B_OK : B_ERROR;
91 }
92 
93 
94 bool
95 PPPoEDevice::Up()
96 {
97 #if DEBUG
98 	printf("PPPoEDevice: Up()\n");
99 #endif
100 
101 	if(InitCheck() != B_OK)
102 		return false;
103 
104 	LockerHelper locker(fLock);
105 
106 	if(IsUp())
107 		return true;
108 
109 	// reset connection settings
110 	memset(fPeer, 0xFF, sizeof(fPeer));
111 
112 	// create PADI
113 	DiscoveryPacket discovery(PADI);
114 	if(fServiceName)
115 		discovery.AddTag(SERVICE_NAME, strlen(fServiceName), fServiceName);
116 	else
117 		discovery.AddTag(SERVICE_NAME, 0, NULL);
118 	discovery.AddTag(HOST_UNIQ, sizeof(uint32), &fHostUniq);
119 	discovery.AddTag(END_OF_LIST, 0, NULL);
120 
121 	// set up PPP header
122 	struct mbuf *packet = discovery.ToMbuf();
123 	if(!packet)
124 		return false;
125 
126 	// create destination
127 	struct sockaddr destination;
128 	memset(&destination, 0, sizeof(destination));
129 	destination.sa_family = AF_UNSPEC;
130 		// raw packet with ethernet header
131 	memcpy(destination.sa_data, fPeer, sizeof(fPeer));
132 
133 	if(EthernetIfnet()->output(EthernetIfnet(), packet, &destination, NULL) != B_OK)
134 		return false;
135 
136 	// check if we are allowed to go up now (user intervention might disallow that)
137 	if(!UpStarted()) {
138 		fState = INITIAL;
139 			// reset state
140 		DownEvent();
141 		return true;
142 	}
143 
144 	fState = PADI_SENT;
145 
146 	fNextTimeout = system_time() + PPPoE_TIMEOUT;
147 
148 	return true;
149 }
150 
151 
152 bool
153 PPPoEDevice::Down()
154 {
155 #if DEBUG
156 	printf("PPPoEDevice: Down()\n");
157 #endif
158 
159 	if(InitCheck() != B_OK)
160 		return false;
161 
162 	LockerHelper locker(fLock);
163 
164 	fNextTimeout = 0;
165 		// disable timeouts
166 
167 	DownStarted();
168 		// this tells StateMachine that DownEvent() does not mean we lost connection
169 
170 	if(!IsUp()) {
171 		DownEvent();
172 		return true;
173 	}
174 
175 	// create PADT
176 	DiscoveryPacket discovery(PADT, SessionID());
177 	discovery.AddTag(END_OF_LIST, 0, NULL);
178 
179 	struct mbuf *packet = discovery.ToMbuf();
180 	if(!packet) {
181 		DownEvent();
182 		return false;
183 	}
184 
185 	// create destination
186 	struct sockaddr destination;
187 	memset(&destination, 0, sizeof(destination));
188 	destination.sa_family = AF_UNSPEC;
189 		// raw packet with ethernet header
190 	memcpy(destination.sa_data, fPeer, sizeof(fPeer));
191 
192 	// reset connection settings
193 	memset(fPeer, 0xFF, sizeof(fPeer));
194 
195 	EthernetIfnet()->output(EthernetIfnet(), packet, &destination, NULL);
196 	DownEvent();
197 
198 	return false;
199 }
200 
201 
202 uint32
203 PPPoEDevice::InputTransferRate() const
204 {
205 	return 10000000;
206 }
207 
208 
209 uint32
210 PPPoEDevice::OutputTransferRate() const
211 {
212 	return 10000000;
213 }
214 
215 
216 uint32
217 PPPoEDevice::CountOutputBytes() const
218 {
219 	// TODO:
220 	// ?look through ethernet queue for outgoing pppoe packets coming from our device?
221 	return 0;
222 }
223 
224 
225 status_t
226 PPPoEDevice::Send(struct mbuf *packet, uint16 protocolNumber = 0)
227 {
228 #if DEBUG
229 	printf("PPPoEDevice: Send()\n");
230 #endif
231 
232 	if(InitCheck() != B_OK || protocolNumber != 0) {
233 		m_freem(packet);
234 		return B_ERROR;
235 	} else if(!packet)
236 		return B_ERROR;
237 
238 	LockerHelper locker(fLock);
239 
240 	if(!IsUp())
241 		return PPP_NO_CONNECTION;
242 
243 	uint16 length = packet->m_flags & M_PKTHDR ? packet->m_pkthdr.len : packet->m_len;
244 
245 	// encapsulate packet into pppoe header
246 	M_PREPEND(packet, PPPoE_HEADER_SIZE);
247 	pppoe_header *header = mtod(packet, pppoe_header*);
248 	header->ethernetHeader.ether_type = ETHERTYPE_PPPOE;
249 	header->version = PPPoE_VERSION;
250 	header->type = PPPoE_TYPE;
251 	header->code = 0x00;
252 	header->sessionID = SessionID();
253 	header->length = htons(length);
254 
255 	// create destination
256 	struct sockaddr destination;
257 	memset(&destination, 0, sizeof(destination));
258 	destination.sa_family = AF_UNSPEC;
259 		// raw packet with ethernet header
260 	memcpy(destination.sa_data, fPeer, sizeof(fPeer));
261 
262 	locker.UnlockNow();
263 
264 	if(EthernetIfnet()->output(EthernetIfnet(), packet, &destination, NULL) != B_OK) {
265 		printf("PPPoEDevice::Send(): EthernetIfnet()->output() failed!\n");
266 		DownEvent();
267 			// DownEvent() without DownStarted() indicates connection lost
268 		return PPP_NO_CONNECTION;
269 	}
270 
271 	return B_OK;
272 }
273 
274 
275 status_t
276 PPPoEDevice::Receive(struct mbuf *packet, uint16 protocolNumber = 0)
277 {
278 #if DEBUG
279 	printf("PPPoEDevice: Receive()\n");
280 #endif
281 
282 	if(InitCheck() != B_OK || IsDown()) {
283 		m_freem(packet);
284 		return B_ERROR;
285 	} else if(!packet)
286 		return B_ERROR;
287 
288 	pppoe_header *header = mtod(packet, pppoe_header*);
289 	if(!header) {
290 		m_freem(packet);
291 		return B_ERROR;
292 	}
293 
294 	status_t result = B_OK;
295 
296 	if(header->ethernetHeader.ether_type == ETHERTYPE_PPPOE) {
297 		if(!IsUp() || header->version != PPPoE_VERSION || header->type != PPPoE_TYPE
298 				|| header->code != 0x0 || header->sessionID != SessionID()) {
299 			m_freem(packet);
300 			return B_ERROR;
301 		}
302 
303 		m_adj(packet, PPPoE_HEADER_SIZE);
304 		return Interface().ReceiveFromDevice(packet);
305 	} else if(header->ethernetHeader.ether_type == ETHERTYPE_PPPOEDISC) {
306 		// we do not need to check HOST_UNIQ tag as this is done in pppoe.cpp
307 		if(header->version != PPPoE_VERSION || header->type != PPPoE_TYPE) {
308 			m_freem(packet);
309 			return B_ERROR;
310 		}
311 
312 		LockerHelper locker(fLock);
313 
314 		if(IsDown()) {
315 			m_freem(packet);
316 			return B_ERROR;
317 		}
318 
319 		DiscoveryPacket discovery(packet);
320 		switch(discovery.Code()) {
321 			case PADO: {
322 				if(fState != PADI_SENT) {
323 					m_freem(packet);
324 					return B_OK;
325 				}
326 
327 				bool hasServiceName = false;
328 				pppoe_tag *tag;
329 				DiscoveryPacket reply(PADR);
330 				for(int32 index = 0; index < discovery.CountTags(); index++) {
331 					tag = discovery.TagAt(index);
332 					switch(tag->type) {
333 						case SERVICE_NAME:
334 							if(!hasServiceName && (!fServiceName
335 									|| !memcmp(tag->data, fServiceName, tag->length))) {
336 								hasServiceName = true;
337 								reply.AddTag(tag->type, tag->length, tag->data);
338 							}
339 						break;
340 
341 						case AC_COOKIE:
342 						case RELAY_SESSION_ID:
343 							reply.AddTag(tag->type, tag->length, tag->data);
344 						break;
345 
346 						case SERVICE_NAME_ERROR:
347 						case AC_SYSTEM_ERROR:
348 						case GENERIC_ERROR:
349 							m_freem(packet);
350 							return B_ERROR;
351 						break;
352 
353 						default:
354 							;
355 					}
356 				}
357 
358 				if(!hasServiceName) {
359 					m_freem(packet);
360 					return B_ERROR;
361 				}
362 
363 				reply.AddTag(END_OF_LIST, 0, NULL);
364 				struct mbuf *replyPacket = reply.ToMbuf();
365 				if(!replyPacket) {
366 					m_freem(packet);
367 					return B_ERROR;
368 				}
369 
370 				memcpy(fPeer, header->ethernetHeader.ether_shost, sizeof(fPeer));
371 
372 				// create destination
373 				struct sockaddr destination;
374 				memset(&destination, 0, sizeof(destination));
375 				destination.sa_family = AF_UNSPEC;
376 					// raw packet with ethernet header
377 				memcpy(destination.sa_data, fPeer, sizeof(fPeer));
378 
379 				if(EthernetIfnet()->output(EthernetIfnet(), replyPacket, &destination,
380 						NULL) != B_OK) {
381 					m_freem(packet);
382 					return B_ERROR;
383 				}
384 
385 				fState = PADR_SENT;
386 				fNextTimeout = system_time() + PPPoE_TIMEOUT;
387 			} break;
388 
389 			case PADS:
390 				if(fState != PADR_SENT
391 						|| memcmp(header->ethernetHeader.ether_shost, fPeer,
392 							sizeof(fPeer))) {
393 					m_freem(packet);
394 					return B_ERROR;
395 				}
396 
397 				fSessionID = header->sessionID;
398 				fState = OPENED;
399 				fNextTimeout = 0;
400 				UpEvent();
401 			break;
402 
403 			case PADT:
404 				if(!IsUp()
405 						|| memcmp(header->ethernetHeader.ether_shost, fPeer,
406 							sizeof(fPeer))
407 						|| header->sessionID != SessionID()) {
408 					m_freem(packet);
409 					return B_ERROR;
410 				}
411 
412 				fState = INITIAL;
413 				fSessionID = 0;
414 				fNextTimeout = 0;
415 				DownEvent();
416 			break;
417 
418 			default:
419 				m_freem(packet);
420 				return B_ERROR;
421 		}
422 	} else
423 		result = B_ERROR;
424 
425 	m_freem(packet);
426 	return result;
427 }
428 
429 
430 void
431 PPPoEDevice::Pulse()
432 {
433 	// We use Pulse() for timeout of connection establishment.
434 
435 	if(fNextTimeout == 0 || IsUp() || IsDown())
436 		return;
437 
438 	LockerHelper locker(fLock);
439 
440 	// check if timed out
441 	if(system_time() >= fNextTimeout)
442 		Up();
443 }
444