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