xref: /haiku/src/add-ons/kernel/network/ppp/pppoe/PPPoEDevice.cpp (revision aa3083e086e5a929c061c72983e09d916c548a38)
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 	DownStarted();
251 		// this tells StateMachine that DownEvent() does not mean we lost connection
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=%ld\n", MTU());
260 		DownEvent();
261 		return false;
262 	}
263 
264 	// create destination
265 	struct ether_header *ethernetHeader;
266 	struct sockaddr destination;
267 	memset(&destination, 0, sizeof(destination));
268 	destination.sa_family = AF_UNSPEC;
269 		// raw packet with ethernet header
270 	ethernetHeader = (struct ether_header*) destination.sa_data;
271 	ethernetHeader->type = ETHER_TYPE_PPPOE_DISCOVERY;
272 	memcpy(ethernetHeader->destination, fPeer, sizeof(fPeer));
273 
274 	// reset connection settings
275 	memset(fPeer, 0xFF, sizeof(fPeer));
276 
277 	EthernetIfnet()->module->send_data(EthernetIfnet(), packet);
278 	DownEvent();
279 
280 	return true;
281 }
282 
283 
284 uint32
285 PPPoEDevice::InputTransferRate() const
286 {
287 	return 10000000;
288 }
289 
290 
291 uint32
292 PPPoEDevice::OutputTransferRate() const
293 {
294 	return 10000000;
295 }
296 
297 
298 uint32
299 PPPoEDevice::CountOutputBytes() const
300 {
301 	// TODO:
302 	// ?look through ethernet queue for outgoing pppoe packets coming from our device?
303 	return 0;
304 }
305 
306 
307 status_t
308 PPPoEDevice::Send(net_buffer *packet, uint16 protocolNumber)
309 {
310 	// Send() is only for PPP packets. PPPoE packets are sent directly to ethernet.
311 
312 	TRACE("PPPoEDevice: Send()\n");
313 
314 	if (!packet)
315 		return B_ERROR;
316 	else if (InitCheck() != B_OK || protocolNumber != 0) {
317 		ERROR("PPPoEDevice::Send(): InitCheck() != B_OK!\n");
318 		gBufferModule->free(packet);
319 		return B_ERROR;
320 	}
321 
322 	if (!IsUp()) {
323 		ERROR("PPPoEDevice::Send(): no connection!\n");
324 		gBufferModule->free(packet);
325 		return PPP_NO_CONNECTION;
326 	}
327 
328 	uint16 length = packet->size;
329 
330 	// encapsulate packet into pppoe header
331 	NetBufferPrepend<pppoe_header> bufferheader(packet);
332 	if (bufferheader.Status() != B_OK)
333 		return B_ERROR;
334 	pppoe_header &header = bufferheader.Data();
335 	header.version = PPPoE_VERSION;
336 	header.type = PPPoE_TYPE;
337 	header.code = 0x00;
338 	header.sessionID = SessionID();
339 	header.length = htons(length);
340 	bufferheader.Sync();
341 
342 	// create ether header
343 	NetBufferPrepend<ether_header> ethernetHeader(packet);
344 	if (ethernetHeader.Status() != B_OK)
345 		return false;
346 	ether_header &ethheader = ethernetHeader.Data();
347 
348 	memcpy(ethheader.destination, fPeer, ETHER_ADDRESS_LENGTH);
349 	memcpy(ethheader.source, fEtherAddr, ETHER_ADDRESS_LENGTH);
350 	ethheader.type = htons(ETHER_TYPE_PPPOE);
351 	ethernetHeader.Sync();
352 	// raw packet with ethernet header
353 
354 	if (!packet)
355 		ERROR("PPPoEDevice::Send(): packet is NULL!\n");
356 
357 	if (EthernetIfnet()->module->send_data(EthernetIfnet(), packet) != B_OK) {
358 		ERROR("PPPoEDevice::Send(): EthernetIfnet()->output() failed!\n");
359 		remove_device(this);
360 		DownEvent();
361 			// DownEvent() without DownStarted() indicates connection lost
362 		return PPP_NO_CONNECTION;
363 	}
364 
365 	return B_OK;
366 }
367 
368 
369 status_t
370 PPPoEDevice::Receive(net_buffer *packet, uint16 protocolNumber)
371 {
372 	TRACE("%s entering %s: protocolNumber:%x\n", __FILE__, __func__, protocolNumber);
373 	if (!packet)
374 		return B_ERROR;
375 
376 	if (InitCheck() != B_OK || IsDown()) {
377 		dprintf("PPPoED InitCheck fail or IsDown\n");
378 		// gBufferModule->free(packet);
379 		dprintf("PPPoEDevice::Receive fail\n");
380 		return B_ERROR;
381 	}
382 
383 	uint8 ethernetSource[6];
384 	struct sockaddr_dl& source = *(struct sockaddr_dl*)packet->source;
385 	memcpy(ethernetSource, source.sdl_data, sizeof(fPeer));
386 
387 	int32 PPP_Packet_Type = B_NET_FRAME_TYPE(source.sdl_type,
388 				ntohs(source.sdl_e_type));
389 //	bufferheader.Remove();
390 //	bufferheader.Sync();
391 
392 	if (PPP_Packet_Type == B_NET_FRAME_TYPE_PPPOE) {
393 		TRACE("ETHER_TYPE_PPPOE\n");
394 		NetBufferHeaderReader<pppoe_header> bufferheader(packet);
395 		if (bufferheader.Status() != B_OK) {
396 			TRACE("creat NetBufferHeaderReader fail\n");
397 			return B_ERROR;
398 		}
399 		pppoe_header &header = bufferheader.Data();
400 		uint16 ppppoe_payload = ntohs(header.length);
401 
402 		if (!IsUp() || header.version != PPPoE_VERSION || header.type != PPPoE_TYPE
403 				|| header.code != 0x0 || header.sessionID != SessionID()) {
404 			// gBufferModule->free(packet);
405 			TRACE("basic pppoe header check fail\n");
406 			return B_ERROR;
407 		}
408 
409 		bufferheader.Remove();
410 		bufferheader.Sync();
411 
412 		// trim the packet according to actual pppoe_payload
413 		gBufferModule->trim(packet, ppppoe_payload);
414 
415 		return Interface().ReceiveFromDevice(packet);
416 	}
417 
418 	if (PPP_Packet_Type == B_NET_FRAME_TYPE_PPPOE_DISCOVERY) {
419 		dprintf("ETHER_TYPE_PPPOE_DISCOVERY\n");
420 		NetBufferHeaderReader<pppoe_header> bufferheader(packet);
421 		if (bufferheader.Status() != B_OK) {
422 			dprintf("create NetBufferHeaderReader fail\n");
423 			return B_ERROR;
424 		}
425 		pppoe_header &header = bufferheader.Data();
426 
427 		// we do not need to check HOST_UNIQ tag as this is done in pppoe.cpp
428 		if (header.version != PPPoE_VERSION || header.type != PPPoE_TYPE) {
429 			// gBufferModule->free(packet);
430 			dprintf("PPPoEDevice::Receive fail version type wrong\n");
431 			return B_ERROR;
432 		}
433 
434 		if (IsDown()) {
435 			// gBufferModule->free(packet);
436 			dprintf("PPPoEDevice::Receive fail PPPoEDev IsDown\n");
437 			return B_ERROR;
438 		}
439 
440 		DiscoveryPacket discovery(packet);
441 		switch(discovery.Code()) {
442 			case PADO: {
443 				dprintf("processing PADO\n");
444 				if (fState != PADI_SENT) {
445 					// gBufferModule->free(packet);
446 					dprintf("PPPoEDevice::Receive faile not PADI_Sent \n");
447 					return B_OK;
448 				}
449 
450 				bool hasServiceName = false, hasACName = false;
451 				pppoe_tag *tag;
452 				DiscoveryPacket reply(PADR);
453 				for (int32 index = 0; index < discovery.CountTags(); index++) {
454 					tag = discovery.TagAt(index);
455 					if (!tag)
456 						continue;
457 
458 					switch (tag->type) {
459 						case SERVICE_NAME:
460 							if (!hasServiceName && (!ServiceName()
461 									|| ((strlen(ServiceName()) == tag->length)
462 										&& !memcmp(tag->data, ServiceName(),
463 											tag->length)))) {
464 								hasServiceName = true;
465 								reply.AddTag(tag->type, tag->data, tag->length);
466 							}
467 						break;
468 
469 						case AC_NAME:
470 							if (!hasACName && (!ACName()
471 									|| ((strlen(ACName()) == tag->length)
472 										&& !memcmp(tag->data, ACName(),
473 											tag->length)))) {
474 								hasACName = true;
475 								reply.AddTag(tag->type, tag->data, tag->length);
476 							}
477 						break;
478 
479 						case AC_COOKIE:
480 						case RELAY_SESSION_ID:
481 							reply.AddTag(tag->type, tag->data, tag->length);
482 						break;
483 
484 						case SERVICE_NAME_ERROR:
485 						case AC_SYSTEM_ERROR:
486 						case GENERIC_ERROR:
487 							// gBufferModule->free(packet);
488 							dprintf("PPPoEDevice::generic error faile\n");
489 							return B_ERROR;
490 						break;
491 
492 						default:
493 							;
494 					}
495 				}
496 
497 				if (!hasServiceName) {
498 					// gBufferModule->free(packet);
499 					dprintf("PPPoEDevice::Receive faile no svc name\n");
500 					return B_ERROR;
501 				}
502 
503 				dprintf("reply.AddTag\n");
504 				reply.AddTag(HOST_UNIQ, &fHostUniq, sizeof(fHostUniq));
505 				reply.AddTag(END_OF_LIST, NULL, 0);
506 				net_buffer *replyPacket = reply.ToNetBuffer(MTU());
507 				if (!replyPacket) {
508 					// gBufferModule->free(packet);
509 					dprintf("PPPoEDevice::Receive fail no reply pack\n");
510 					return B_ERROR;
511 				}
512 
513 				memcpy(fPeer, ethernetSource, ETHER_ADDRESS_LENGTH);
514 
515 				// create ether header
516 				NetBufferPrepend<ether_header> ethernetHeader(replyPacket);
517 				if (ethernetHeader.Status() != B_OK)
518 					return false;
519 				ether_header &header = ethernetHeader.Data();
520 
521 				memcpy(header.destination, fPeer, ETHER_ADDRESS_LENGTH);
522 				memcpy(header.source, fEtherAddr, ETHER_ADDRESS_LENGTH);
523 				header.type=htons(ETHER_TYPE_PPPOE_DISCOVERY);
524 				ethernetHeader.Sync();
525 				// raw packet with ethernet header
526 
527 				fState = PADR_SENT;
528 
529 				if (EthernetIfnet()->module->send_data(EthernetIfnet(), replyPacket) != B_OK) {
530 					gBufferModule->free(replyPacket);
531 					dprintf("PPPoEDevice::Receive fail send PADR\n");
532 					return B_ERROR;
533 				}
534 
535 				fNextTimeout = system_time() + PPPoE_TIMEOUT;
536 			}
537 			break;
538 			case PADS:
539 				dprintf("procesing PADS\n");
540 				if (fState != PADR_SENT
541 					|| memcmp(ethernetSource, fPeer, sizeof(fPeer))) {
542 					// gBufferModule->free(packet);
543 					dprintf("PPPoEDevice::Receive PADS but no PADR sent\n");
544 					return B_ERROR;
545 				}
546 
547 				fSessionID = header.sessionID;
548 				fState = OPENED;
549 				fNextTimeout = 0;
550 				UpEvent();
551 			break;
552 
553 			case PADT:
554 				dprintf("procesing PADT\n");
555 				if (!IsUp()
556 						|| memcmp(ethernetSource, fPeer, sizeof(fPeer))
557 						|| header.sessionID != SessionID()) {
558 					// gBufferModule->free(packet);
559 					dprintf("PPPoEDevice::Receive fail not up yet\n");
560 					return B_ERROR;
561 				}
562 
563 				fState = INITIAL;
564 				fAttempts = 0;
565 				fSessionID = 0;
566 				fNextTimeout = 0;
567 				remove_device(this);
568 				DownEvent();
569 			break;
570 
571 			default:
572 				dprintf("PPPoEDevice::Receive fail unknown pppoed code\n");
573 				// gBufferModule->free(packet);
574 				return B_ERROR;
575 		}
576 	}
577 
578 	dprintf("PPPoEDevice::Receive PADX fine!\n");
579 	// gBufferModule->free(packet);
580 	return B_OK;
581 }
582 
583 
584 void
585 PPPoEDevice::Pulse()
586 {
587 	// We use Pulse() for timeout of connection establishment.
588 	if (fNextTimeout == 0 || IsUp() || IsDown())
589 		return;
590 
591 	// check if timed out
592 	if (system_time() >= fNextTimeout) {
593 		if (!Up())
594 			UpFailedEvent();
595 	}
596 }
597