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