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