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