xref: /haiku/src/add-ons/kernel/network/ppp/pppoe/PPPoEDevice.cpp (revision 6aff37d1c79e20748c683ae224bd629f88a5b0be)
1 /*
2  * Copyright 2003-2006, Waldemar Kornewald <wkornew@gmx.net>
3  * Distributed under the terms of the MIT License.
4  */
5 
6 #include <cstdlib>
7 
8 #include <ByteOrder.h>
9 #include <net/if_dl.h>
10 #include <net_stack.h>
11 
12 #include <ethernet.h>
13 #include <ether_driver.h>
14 
15 #include "PPPoEDevice.h"
16 #include "DiscoveryPacket.h"
17 
18 // from libkernelppp
19 #include <settings_tools.h>
20 
21 extern net_stack_module_info *gStackModule;
22 extern net_buffer_module_info *gBufferModule;
23 extern status_t
24 pppoe_input(void *cookie, net_device *_device, net_buffer *packet);
25 
26 #if DEBUG
27 static char sDigits[] = "0123456789ABCDEF";
28 void
29 dump_packet(net_buffer *packet)
30 {
31 	if (!packet)
32 		return;
33 
34 	BufferHeaderReader<uint8> bufferheader(packet);
35 	if (bufferheader.Status() != B_OK)
36 		return;
37 	uint8 &buffer = bufferheader.Data();
38 	uint8 *data = &buffer;
39 	uint8 buffer[33];
40 	uint8 bufferIndex = 0;
41 
42 	TRACE("Dumping packet;len=%ld;pkthdr.len=%d\n", packet->m_len,
43 		packet->m_flags & M_PKTHDR ? packet->m_pkthdr.len : -1);
44 
45 	for (uint32 index = 0; index < packet->m_len; index++) {
46 		buffer[bufferIndex++] = sDigits[data[index] >> 4];
47 		buffer[bufferIndex++] = sDigits[data[index] & 0x0F];
48 		if (bufferIndex == 32 || index == packet->m_len - 1) {
49 			buffer[bufferIndex] = 0;
50 			TRACE("%s\n", buffer);
51 			bufferIndex = 0;
52 		}
53 	}
54 }
55 
56 
57 #endif
58 
59 
60 PPPoEDevice::PPPoEDevice(KPPPInterface& interface, driver_parameter *settings)
61 	: KPPPDevice("PPPoE", PPPoE_HEADER_SIZE + ETHER_HDR_LEN, interface, settings),
62 	fEthernetIfnet(NULL),
63 	fSessionID(0),
64 	fHostUniq(NewHostUniq()),
65 	fACName(NULL),
66 	fServiceName(NULL),
67 	fAttempts(0),
68 	fNextTimeout(0),
69 	fState(INITIAL)
70 {
71 #if DEBUG
72 	TRACE("PPPoEDevice: Constructor\n");
73 	if (!settings || !settings->parameters)
74 		TRACE("PPPoEDevice::ctor: No settings!\n");
75 #endif
76 
77 	interface.SetPFCOptions(PPP_ALLOW_PFC);
78 		// we do not want to fail just because the other side requests PFC
79 
80 	memset(fPeer, 0xFF, sizeof(fPeer));
81 
82 	SetMTU(1494);
83 		// MTU size does not contain PPP header
84 
85 	if (!settings)
86 		dprintf("%s::%s: settings is NULL\n", __FILE__, __func__);
87 
88 	// find ethernet device
89 	finterfaceName = get_parameter_value(PPPoE_INTERFACE_KEY, settings);
90 	TRACE("%s::%s: finterfaceName is %s\n", __FILE__, __func__,
91 			finterfaceName ? finterfaceName : "NULL");
92 
93 	fACName = get_parameter_value(PPPoE_AC_NAME_KEY, settings);
94 	TRACE("fACName is %s\n", fACName ? fACName : "NULL");
95 
96 	fServiceName = get_parameter_value(PPPoE_SERVICE_NAME_KEY, settings);
97 	TRACE("fServiceName is %s\n", fServiceName ? fServiceName : "NULL");
98 
99 	// fEthernetIfnet = FindPPPoEInterface(interfaceName);
100 
101 #if DEBUG
102 	if (!fEthernetIfnet)
103 		TRACE("PPPoEDevice::ctor: could not find ethernet interface\n");
104 	else
105 		TRACE("%s::%s: Find Ethernet device", __FILE__, __func__);
106 #endif
107 }
108 
109 
110 status_t
111 PPPoEDevice::InitCheck() const
112 {
113 	if (KPPPDevice::InitCheck() != B_OK)
114 		dprintf("%s::%s: KPPPDevice::InitCheck() has error\n", __FILE__, __func__);
115 
116 	return KPPPDevice::InitCheck() == B_OK ? B_OK : B_ERROR;
117 }
118 
119 
120 bool
121 PPPoEDevice::Up()
122 {
123 	TRACE("PPPoEDevice: Up()\n");
124 
125 	if (InitCheck() != B_OK)
126 		return false;
127 
128 	if (IsUp())
129 		return true;
130 
131 	fEthernetIfnet = FindPPPoEInterface(finterfaceName);
132 
133 	if (fEthernetIfnet == NULL) {
134 		dprintf("%s::%s: fEthernetIfnet %s not found\n", __FILE__, __func__, finterfaceName);
135 		return false;
136 	}
137 
138 	memcpy(fEtherAddr, fEthernetIfnet->address.data, ETHER_ADDRESS_LENGTH);
139 	dprintf("ppp's corresponding addr is %02x:%02x:%02x:%02x:%02x:%02x\n",
140 		fEtherAddr[0], fEtherAddr[1], fEtherAddr[2], fEtherAddr[3],
141 		fEtherAddr[4], fEtherAddr[5]);
142 
143 	if (fEthernetIfnet->module == NULL) {
144 		dprintf("%s::%s: fEthernetIfnet->module not found\n", __FILE__,
145 			__func__);
146 		return false;
147 	}
148 
149 	add_device(this);
150 
151 	fState = INITIAL;
152 		// reset state
153 
154 	if (fAttempts > PPPoE_MAX_ATTEMPTS) {
155 		fAttempts = 0;
156 		return false;
157 	}
158 
159 	++fAttempts;
160 	// reset connection settings
161 	memset(fPeer, 0xFF, sizeof(fPeer));
162 
163 	// create PADI
164 	DiscoveryPacket discovery(PADI);
165 	if (ServiceName())
166 		discovery.AddTag(SERVICE_NAME, ServiceName(), strlen(ServiceName()));
167 	else
168 		discovery.AddTag(SERVICE_NAME, NULL, 0);
169 	discovery.AddTag(HOST_UNIQ, &fHostUniq, sizeof(fHostUniq));
170 	discovery.AddTag(END_OF_LIST, NULL, 0);
171 
172 	// set up PPP header
173 	net_buffer *packet = discovery.ToNetBuffer(MTU());
174 	if (!packet)
175 		return false;
176 
177 	// create ether header
178 	NetBufferPrepend<ether_header> ethernetHeader(packet);
179 	if (ethernetHeader.Status() != B_OK)
180 		return false;
181 	ether_header &header = ethernetHeader.Data();
182 
183 	memset(header.destination, 0xff, ETHER_ADDRESS_LENGTH);
184 	memcpy(header.source, fEtherAddr, ETHER_ADDRESS_LENGTH);
185 	header.type = htons(ETHER_TYPE_PPPOE_DISCOVERY);
186 	ethernetHeader.Sync();
187 	// raw packet with ethernet header
188 
189 	// check if we are allowed to go up now (user intervention might disallow that)
190 	if (fAttempts > 0 && !UpStarted()) {
191 		fAttempts = 0;
192 		remove_device(this);
193 		DownEvent();
194 		return true;
195 			// there was no error
196 	}
197 
198 	fState = PADI_SENT;
199 		// needed before sending, otherwise we might not get all packets
200 
201 	status_t status = gStackModule->register_device_handler(fEthernetIfnet,
202 		B_NET_FRAME_TYPE_PPPOE_DISCOVERY, &pppoe_input, NULL);
203 	if (status != B_OK)
204 		return false;
205 
206 	status = gStackModule->register_device_handler(fEthernetIfnet,
207 		B_NET_FRAME_TYPE_PPPOE, &pppoe_input, NULL);
208 	if (status != B_OK)
209 		return false;
210 
211 	if (EthernetIfnet()->module->send_data(EthernetIfnet(), packet) != B_OK) {
212 		fState = INITIAL;
213 		fAttempts = 0;
214 		ERROR("PPPoEDevice::Up(): EthernetIfnet()->output() failed!\n");
215 		return false;
216 	}
217 
218 	dprintf("PPPoEDevice::Up(): EthernetIfnet()->output() success!\n");
219 	fNextTimeout = system_time() + PPPoE_TIMEOUT;
220 
221 	return true;
222 }
223 
224 
225 bool
226 PPPoEDevice::Down()
227 {
228 	TRACE("PPPoEDevice: Down()\n");
229 
230 	gStackModule->unregister_device_handler(fEthernetIfnet,
231 		B_NET_FRAME_TYPE_PPPOE_DISCOVERY);
232 	gStackModule->unregister_device_handler(fEthernetIfnet,
233 		B_NET_FRAME_TYPE_PPPOE);
234 
235 	remove_device(this);
236 
237 	if (InitCheck() != B_OK)
238 		return false;
239 
240 	fState = INITIAL;
241 	fAttempts = 0;
242 	fNextTimeout = 0;
243 		// disable timeouts
244 
245 	if (!IsUp()) {
246 		DownEvent();
247 		return true;
248 	}
249 
250 	// this tells StateMachine that DownEvent() does not mean we lost connection
251 	DownStarted();
252 
253 	// create PADT
254 	DiscoveryPacket discovery(PADT, SessionID());
255 	discovery.AddTag(END_OF_LIST, NULL, 0);
256 
257 	net_buffer *packet = discovery.ToNetBuffer(MTU());
258 	if (!packet) {
259 		ERROR("PPPoEDevice::Down(): ToNetBuffer() failed; MTU=%" B_PRIu32 "\n",
260 			MTU());
261 		DownEvent();
262 		return false;
263 	}
264 
265 	// create destination
266 	struct ether_header *ethernetHeader;
267 	struct sockaddr destination;
268 	memset(&destination, 0, sizeof(destination));
269 	destination.sa_family = AF_UNSPEC;
270 		// raw packet with ethernet header
271 	ethernetHeader = (struct ether_header*) destination.sa_data;
272 	ethernetHeader->type = ETHER_TYPE_PPPOE_DISCOVERY;
273 	memcpy(ethernetHeader->destination, fPeer, sizeof(fPeer));
274 
275 	// reset connection settings
276 	memset(fPeer, 0xFF, sizeof(fPeer));
277 
278 	EthernetIfnet()->module->send_data(EthernetIfnet(), packet);
279 	DownEvent();
280 
281 	return true;
282 }
283 
284 
285 uint32
286 PPPoEDevice::InputTransferRate() const
287 {
288 	return 10000000;
289 }
290 
291 
292 uint32
293 PPPoEDevice::OutputTransferRate() const
294 {
295 	return 10000000;
296 }
297 
298 
299 uint32
300 PPPoEDevice::CountOutputBytes() const
301 {
302 	// TODO:
303 	// ?look through ethernet queue for outgoing pppoe packets coming from our device?
304 	return 0;
305 }
306 
307 
308 status_t
309 PPPoEDevice::Send(net_buffer *packet, uint16 protocolNumber)
310 {
311 	// Send() is only for PPP packets. PPPoE packets are sent directly to ethernet.
312 
313 	TRACE("PPPoEDevice: Send()\n");
314 
315 	if (!packet)
316 		return B_ERROR;
317 	else if (InitCheck() != B_OK || protocolNumber != 0) {
318 		ERROR("PPPoEDevice::Send(): InitCheck() != B_OK!\n");
319 		gBufferModule->free(packet);
320 		return B_ERROR;
321 	}
322 
323 	if (!IsUp()) {
324 		ERROR("PPPoEDevice::Send(): no connection!\n");
325 		gBufferModule->free(packet);
326 		return PPP_NO_CONNECTION;
327 	}
328 
329 	uint16 length = packet->size;
330 
331 	// encapsulate packet into pppoe header
332 	NetBufferPrepend<pppoe_header> bufferheader(packet);
333 	if (bufferheader.Status() != B_OK)
334 		return B_ERROR;
335 	pppoe_header &header = bufferheader.Data();
336 	header.version = PPPoE_VERSION;
337 	header.type = PPPoE_TYPE;
338 	header.code = 0x00;
339 	header.sessionID = SessionID();
340 	header.length = htons(length);
341 	bufferheader.Sync();
342 
343 	// create ether header
344 	NetBufferPrepend<ether_header> ethernetHeader(packet);
345 	if (ethernetHeader.Status() != B_OK)
346 		return false;
347 	ether_header &ethheader = ethernetHeader.Data();
348 
349 	memcpy(ethheader.destination, fPeer, ETHER_ADDRESS_LENGTH);
350 	memcpy(ethheader.source, fEtherAddr, ETHER_ADDRESS_LENGTH);
351 	ethheader.type = htons(ETHER_TYPE_PPPOE);
352 	ethernetHeader.Sync();
353 	// raw packet with ethernet header
354 
355 	if (!packet)
356 		ERROR("PPPoEDevice::Send(): packet is NULL!\n");
357 
358 	if (EthernetIfnet()->module->send_data(EthernetIfnet(), packet) != B_OK) {
359 		ERROR("PPPoEDevice::Send(): EthernetIfnet()->output() failed!\n");
360 		remove_device(this);
361 		DownEvent();
362 			// DownEvent() without DownStarted() indicates connection lost
363 		return PPP_NO_CONNECTION;
364 	}
365 
366 	return B_OK;
367 }
368 
369 
370 status_t
371 PPPoEDevice::Receive(net_buffer *packet, uint16 protocolNumber)
372 {
373 	TRACE("%s entering %s: protocolNumber:%x\n", __FILE__, __func__, protocolNumber);
374 	if (!packet)
375 		return B_ERROR;
376 
377 	if (InitCheck() != B_OK || IsDown()) {
378 		dprintf("PPPoED InitCheck fail or IsDown\n");
379 		// gBufferModule->free(packet);
380 		dprintf("PPPoEDevice::Receive fail\n");
381 		return B_ERROR;
382 	}
383 
384 	uint8 ethernetSource[6];
385 	struct sockaddr_dl& source = *(struct sockaddr_dl*)packet->source;
386 	memcpy(ethernetSource, source.sdl_data, sizeof(fPeer));
387 
388 	int32 PPP_Packet_Type = B_NET_FRAME_TYPE(source.sdl_type,
389 				ntohs(source.sdl_e_type));
390 //	bufferheader.Remove();
391 //	bufferheader.Sync();
392 
393 	if (PPP_Packet_Type == B_NET_FRAME_TYPE_PPPOE) {
394 		TRACE("ETHER_TYPE_PPPOE\n");
395 		NetBufferHeaderReader<pppoe_header> bufferheader(packet);
396 		if (bufferheader.Status() != B_OK) {
397 			TRACE("creat NetBufferHeaderReader fail\n");
398 			return B_ERROR;
399 		}
400 		pppoe_header &header = bufferheader.Data();
401 		uint16 ppppoe_payload = ntohs(header.length);
402 
403 		if (!IsUp() || header.version != PPPoE_VERSION || header.type != PPPoE_TYPE
404 				|| header.code != 0x0 || header.sessionID != SessionID()) {
405 			// gBufferModule->free(packet);
406 			TRACE("basic pppoe header check fail\n");
407 			return B_ERROR;
408 		}
409 
410 		bufferheader.Remove();
411 		bufferheader.Sync();
412 
413 		// trim the packet according to actual pppoe_payload
414 		gBufferModule->trim(packet, ppppoe_payload);
415 
416 		return Interface().ReceiveFromDevice(packet);
417 	}
418 
419 	if (PPP_Packet_Type == B_NET_FRAME_TYPE_PPPOE_DISCOVERY) {
420 		dprintf("ETHER_TYPE_PPPOE_DISCOVERY\n");
421 		NetBufferHeaderReader<pppoe_header> bufferheader(packet);
422 		if (bufferheader.Status() != B_OK) {
423 			dprintf("create NetBufferHeaderReader fail\n");
424 			return B_ERROR;
425 		}
426 		pppoe_header &header = bufferheader.Data();
427 
428 		// we do not need to check HOST_UNIQ tag as this is done in pppoe.cpp
429 		if (header.version != PPPoE_VERSION || header.type != PPPoE_TYPE) {
430 			// gBufferModule->free(packet);
431 			dprintf("PPPoEDevice::Receive fail version type wrong\n");
432 			return B_ERROR;
433 		}
434 
435 		if (IsDown()) {
436 			// gBufferModule->free(packet);
437 			dprintf("PPPoEDevice::Receive fail PPPoEDev IsDown\n");
438 			return B_ERROR;
439 		}
440 
441 		DiscoveryPacket discovery(packet);
442 		switch(discovery.Code()) {
443 			case PADO: {
444 				dprintf("processing PADO\n");
445 				if (fState != PADI_SENT) {
446 					// gBufferModule->free(packet);
447 					dprintf("PPPoEDevice::Receive faile not PADI_Sent \n");
448 					return B_OK;
449 				}
450 
451 				bool hasServiceName = false, hasACName = false;
452 				pppoe_tag *tag;
453 				DiscoveryPacket reply(PADR);
454 				for (int32 index = 0; index < discovery.CountTags(); index++) {
455 					tag = discovery.TagAt(index);
456 					if (!tag)
457 						continue;
458 
459 					switch (tag->type) {
460 						case SERVICE_NAME:
461 							if (!hasServiceName && (!ServiceName()
462 									|| ((strlen(ServiceName()) == tag->length)
463 										&& !memcmp(tag->data, ServiceName(),
464 											tag->length)))) {
465 								hasServiceName = true;
466 								reply.AddTag(tag->type, tag->data, tag->length);
467 							}
468 						break;
469 
470 						case AC_NAME:
471 							if (!hasACName && (!ACName()
472 									|| ((strlen(ACName()) == tag->length)
473 										&& !memcmp(tag->data, ACName(),
474 											tag->length)))) {
475 								hasACName = true;
476 								reply.AddTag(tag->type, tag->data, tag->length);
477 							}
478 						break;
479 
480 						case AC_COOKIE:
481 						case RELAY_SESSION_ID:
482 							reply.AddTag(tag->type, tag->data, tag->length);
483 						break;
484 
485 						case SERVICE_NAME_ERROR:
486 						case AC_SYSTEM_ERROR:
487 						case GENERIC_ERROR:
488 							// gBufferModule->free(packet);
489 							dprintf("PPPoEDevice::generic error faile\n");
490 							return B_ERROR;
491 						break;
492 
493 						default:
494 							;
495 					}
496 				}
497 
498 				if (!hasServiceName) {
499 					// gBufferModule->free(packet);
500 					dprintf("PPPoEDevice::Receive faile no svc name\n");
501 					return B_ERROR;
502 				}
503 
504 				dprintf("reply.AddTag\n");
505 				reply.AddTag(HOST_UNIQ, &fHostUniq, sizeof(fHostUniq));
506 				reply.AddTag(END_OF_LIST, NULL, 0);
507 				net_buffer *replyPacket = reply.ToNetBuffer(MTU());
508 				if (!replyPacket) {
509 					// gBufferModule->free(packet);
510 					dprintf("PPPoEDevice::Receive fail no reply pack\n");
511 					return B_ERROR;
512 				}
513 
514 				memcpy(fPeer, ethernetSource, ETHER_ADDRESS_LENGTH);
515 
516 				// create ether header
517 				NetBufferPrepend<ether_header> ethernetHeader(replyPacket);
518 				if (ethernetHeader.Status() != B_OK)
519 					return false;
520 				ether_header &header = ethernetHeader.Data();
521 
522 				memcpy(header.destination, fPeer, ETHER_ADDRESS_LENGTH);
523 				memcpy(header.source, fEtherAddr, ETHER_ADDRESS_LENGTH);
524 				header.type=htons(ETHER_TYPE_PPPOE_DISCOVERY);
525 				ethernetHeader.Sync();
526 				// raw packet with ethernet header
527 
528 				fState = PADR_SENT;
529 
530 				if (EthernetIfnet()->module->send_data(EthernetIfnet(), replyPacket) != B_OK) {
531 					gBufferModule->free(replyPacket);
532 					dprintf("PPPoEDevice::Receive fail send PADR\n");
533 					return B_ERROR;
534 				}
535 
536 				fNextTimeout = system_time() + PPPoE_TIMEOUT;
537 			}
538 			break;
539 			case PADS:
540 				dprintf("procesing PADS\n");
541 				if (fState != PADR_SENT
542 					|| memcmp(ethernetSource, fPeer, sizeof(fPeer))) {
543 					// gBufferModule->free(packet);
544 					dprintf("PPPoEDevice::Receive PADS but no PADR sent\n");
545 					return B_ERROR;
546 				}
547 
548 				fSessionID = header.sessionID;
549 				fState = OPENED;
550 				fNextTimeout = 0;
551 				UpEvent();
552 			break;
553 
554 			case PADT:
555 				dprintf("procesing PADT\n");
556 				if (!IsUp()
557 						|| memcmp(ethernetSource, fPeer, sizeof(fPeer))
558 						|| header.sessionID != SessionID()) {
559 					// gBufferModule->free(packet);
560 					dprintf("PPPoEDevice::Receive fail not up yet\n");
561 					return B_ERROR;
562 				}
563 
564 				fState = INITIAL;
565 				fAttempts = 0;
566 				fSessionID = 0;
567 				fNextTimeout = 0;
568 				remove_device(this);
569 				DownEvent();
570 			break;
571 
572 			default:
573 				dprintf("PPPoEDevice::Receive fail unknown pppoed code\n");
574 				// gBufferModule->free(packet);
575 				return B_ERROR;
576 		}
577 	}
578 
579 	dprintf("PPPoEDevice::Receive PADX fine!\n");
580 	// gBufferModule->free(packet);
581 	return B_OK;
582 }
583 
584 
585 void
586 PPPoEDevice::Pulse()
587 {
588 	// We use Pulse() for timeout of connection establishment.
589 	if (fNextTimeout == 0 || IsUp() || IsDown())
590 		return;
591 
592 	// check if timed out
593 	if (system_time() >= fNextTimeout) {
594 		if (!Up())
595 			UpFailedEvent();
596 	}
597 }
598