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
22extern net_stack_module_info *gStackModule;
23extern net_buffer_module_info *gBufferModule;
24extern status_t
25pppoe_input(void *cookie, net_device *_device, net_buffer *packet);
26
27#if DEBUG
28static char sDigits[] = "0123456789ABCDEF";
29void
30dump_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
61PPPoEDevice::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
111status_t
112PPPoEDevice::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
121bool
122PPPoEDevice::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
226bool
227PPPoEDevice::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
286uint32
287PPPoEDevice::InputTransferRate() const
288{
289	return 10000000;
290}
291
292
293uint32
294PPPoEDevice::OutputTransferRate() const
295{
296	return 10000000;
297}
298
299
300uint32
301PPPoEDevice::CountOutputBytes() const
302{
303	// TODO:
304	// ?look through ethernet queue for outgoing pppoe packets coming from our device?
305	return 0;
306}
307
308
309status_t
310PPPoEDevice::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
371status_t
372PPPoEDevice::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
586void
587PPPoEDevice::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