xref: /haiku/src/add-ons/kernel/network/ppp/pppoe/pppoe.cpp (revision 67bce78b48ed6d01b5a8eef89f5694c372b7e0a1)
1 //-----------------------------------------------------------------------
2 //  This software is part of the OpenBeOS distribution and is covered
3 //  by the OpenBeOS license.
4 //
5 //  Copyright (c) 2003-2004 Waldemar Kornewald, Waldemar.Kornewald@web.de
6 //-----------------------------------------------------------------------
7 
8 #include <KernelExport.h>
9 #include <driver_settings.h>
10 #include <core_funcs.h>
11 #include <net_module.h>
12 
13 #include <ethernet_module.h>
14 
15 #include <KPPPInterface.h>
16 #include <KPPPModule.h>
17 #include <KPPPUtils.h>
18 #include <LockerHelper.h>
19 
20 #include "PPPoEDevice.h"
21 #include "DiscoveryPacket.h"
22 
23 
24 typedef struct pppoe_query {
25 	ifnet *ethernetIfnet;
26 	uint32 hostUniq;
27 	thread_id receiver;
28 } pppoe_query;
29 
30 #define PPPoE_MODULE_NAME		NETWORK_MODULES_ROOT "ppp/pppoe"
31 
32 struct core_module_info *core = NULL;
33 static struct ethernet_module_info *sEthernet;
34 static int32 sHostUniq = 0;
35 status_t std_ops(int32 op, ...);
36 
37 static BLocker sLock;
38 static TemplateList<PPPoEDevice*> *sDevices;
39 static TemplateList<pppoe_query*> *sQueries;
40 
41 
42 static
43 void
44 SendQueryPacket(pppoe_query *query, DiscoveryPacket& discovery)
45 {
46 	char data[PPPoE_QUERY_REPORT_SIZE];
47 	uint32 position = sizeof(uint32);
48 	pppoe_tag *acName = discovery.TagWithType(AC_NAME);
49 
50 	if(acName) {
51 		if(acName->length >= PPPoE_QUERY_REPORT_SIZE)
52 			return;
53 
54 		memcpy(data + position, acName->data, acName->length);
55 		position += acName->length;
56 	}
57 
58 	data[position++] = 0;
59 
60 	pppoe_tag *tag;
61 	for(int32 index = 0; index < discovery.CountTags(); index++) {
62 		tag = discovery.TagAt(index);
63 		if(tag && tag->type == SERVICE_NAME) {
64 			if(position + tag->length >= PPPoE_QUERY_REPORT_SIZE)
65 				return;
66 
67 			memcpy(data + position, tag->data, tag->length);
68 			position += tag->length;
69 			data[position++] = 0;
70 		}
71 	}
72 
73 	memcpy(data, &position, sizeof(uint32));
74 		// add the total length
75 
76 	send_data_with_timeout(query->receiver, PPPoE_QUERY_REPORT, data,
77 		PPPoE_QUERY_REPORT_SIZE, 700000);
78 }
79 
80 
81 ifnet*
82 FindPPPoEInterface(const char *name)
83 {
84 	if(!name)
85 		return NULL;
86 
87 	ifnet *current = get_interfaces();
88 	for(; current; current = current->if_next) {
89 		if(current->if_type == IFT_ETHER && current->if_name
90 				&& !strcmp(current->if_name, name))
91 			return current;
92 	}
93 
94 	return NULL;
95 }
96 
97 uint32
98 NewHostUniq()
99 {
100 	return (uint32) atomic_add(&sHostUniq, 1);
101 }
102 
103 
104 void
105 add_device(PPPoEDevice *device)
106 {
107 #if DEBUG
108 	dprintf("PPPoE: add_device()\n");
109 #endif
110 
111 	LockerHelper locker(sLock);
112 	sDevices->AddItem(device);
113 }
114 
115 
116 void
117 remove_device(PPPoEDevice *device)
118 {
119 #if DEBUG
120 	dprintf("PPPoE: remove_device()\n");
121 #endif
122 
123 	LockerHelper locker(sLock);
124 	sDevices->RemoveItem(device);
125 }
126 
127 
128 static
129 void
130 pppoe_input(struct mbuf *packet)
131 {
132 	if(!packet)
133 		return;
134 
135 	ifnet *sourceIfnet = packet->m_pkthdr.rcvif;
136 	complete_pppoe_header *header = mtod(packet, complete_pppoe_header*);
137 	PPPoEDevice *device;
138 	pppoe_query *query;
139 
140 	LockerHelper locker(sLock);
141 
142 	if(header->ethernetHeader.ether_type == ETHERTYPE_PPPOEDISC
143 			&& header->pppoeHeader.length <= PPPoE_QUERY_REPORT_SIZE) {
144 		for(int32 index = 0; index < sDevices->CountItems(); index++) {
145 			query = sQueries->ItemAt(index);
146 
147 			if(query && query->ethernetIfnet == sourceIfnet) {
148 				if(header->pppoeHeader.code == PADO) {
149 					DiscoveryPacket discovery(packet, ETHER_HDR_LEN);
150 					if(discovery.InitCheck() != B_OK) {
151 						dprintf("PPPoE: received corrupted discovery packet!\n");
152 						m_freem(packet);
153 						return;
154 					}
155 
156 					pppoe_tag *hostTag = discovery.TagWithType(HOST_UNIQ);
157 					if(hostTag && hostTag->length == 4
158 							&& *((uint32*)hostTag->data) == query->hostUniq) {
159 						SendQueryPacket(query, discovery);
160 						m_freem(packet);
161 						return;
162 					}
163 				}
164 			}
165 		}
166 	}
167 
168 	for(int32 index = 0; index < sDevices->CountItems(); index++) {
169 		device = sDevices->ItemAt(index);
170 
171 		if(device && device->EthernetIfnet() == sourceIfnet) {
172 			if(header->ethernetHeader.ether_type == ETHERTYPE_PPPOE
173 					&& header->pppoeHeader.sessionID == device->SessionID()) {
174 #if DEBUG
175 				dprintf("PPPoE: session packet\n");
176 #endif
177 				device->Receive(packet);
178 				return;
179 			} else if(header->ethernetHeader.ether_type == ETHERTYPE_PPPOEDISC
180 					&& header->pppoeHeader.code != PADI
181 					&& header->pppoeHeader.code != PADR
182 					&& !device->IsDown()) {
183 #if DEBUG
184 				dprintf("PPPoE: discovery packet\n");
185 #endif
186 				DiscoveryPacket discovery(packet, ETHER_HDR_LEN);
187 				if(discovery.InitCheck() != B_OK) {
188 					dprintf("PPPoE: received corrupted discovery packet!\n");
189 					m_freem(packet);
190 					return;
191 				}
192 
193 				pppoe_tag *tag = discovery.TagWithType(HOST_UNIQ);
194 				if(header->pppoeHeader.code == PADT || (tag && tag->length == 4
195 						&& *((uint32*)tag->data) == device->HostUniq())) {
196 					device->Receive(packet);
197 					return;
198 				}
199 			}
200 		}
201 	}
202 
203 	dprintf("PPPoE: No device found for packet from: %s\n", sourceIfnet->if_name);
204 	m_freem(packet);
205 }
206 
207 
208 static
209 bool
210 add_to(KPPPInterface& mainInterface, KPPPInterface *subInterface,
211 	driver_parameter *settings, ppp_module_key_type type)
212 {
213 	if(mainInterface.Mode() != PPP_CLIENT_MODE || type != PPP_DEVICE_KEY_TYPE)
214 		return B_ERROR;
215 
216 	PPPoEDevice *device;
217 	bool success;
218 	if(subInterface) {
219 		device = new PPPoEDevice(*subInterface, settings);
220 		success = subInterface->SetDevice(device);
221 	} else {
222 		device = new PPPoEDevice(mainInterface, settings);
223 		success = mainInterface.SetDevice(device);
224 	}
225 
226 #if DEBUG
227 	dprintf("PPPoE: add_to(): %s\n",
228 		success && device && device->InitCheck() == B_OK ? "OK" : "ERROR");
229 #endif
230 
231 	return success && device && device->InitCheck() == B_OK;
232 }
233 
234 
235 static
236 status_t
237 control(uint32 op, void *data, size_t length)
238 {
239 	switch(op) {
240 		case PPPoE_GET_INTERFACES: {
241 			int32 position = 0, count = 0;
242 			char *names = (char*) data;
243 
244 			ifnet *current = get_interfaces();
245 			for(; current; current = current->if_next) {
246 				if(current->if_type == IFT_ETHER && current->if_name) {
247 					if(position + strlen(current->if_name) + 1 > length)
248 						return B_NO_MEMORY;
249 
250 					strcpy(names + position, current->if_name);
251 					position += strlen(current->if_name);
252 					names[position++] = 0;
253 					++count;
254 				}
255 			}
256 
257 			return count;
258 		}
259 
260 		case PPPoE_QUERY_SERVICES: {
261 			// XXX: as all modules are loaded on-demand we must wait for the results
262 
263 			if(!data || length != sizeof(pppoe_query_request))
264 				return B_ERROR;
265 
266 			pppoe_query_request *request = (pppoe_query_request*) data;
267 
268 			pppoe_query query;
269 			query.ethernetIfnet = FindPPPoEInterface(request->interfaceName);
270 			if(!query.ethernetIfnet)
271 				return B_BAD_VALUE;
272 
273 			query.hostUniq = NewHostUniq();
274 			query.receiver = request->receiver;
275 
276 			sLock.Lock();
277 			sQueries->AddItem(&query);
278 			sLock.Unlock();
279 
280 			snooze(2000000);
281 				// wait two seconds for results
282 
283 			sLock.Lock();
284 			sQueries->RemoveItem(&query);
285 			sLock.Unlock();
286 		} break;
287 
288 		default:
289 			return B_ERROR;
290 	}
291 
292 	return B_OK;
293 }
294 
295 
296 static ppp_module_info pppoe_module = {
297 	{
298 		PPPoE_MODULE_NAME,
299 		0,
300 		std_ops
301 	},
302 	control,
303 	add_to
304 };
305 
306 
307 _EXPORT
308 status_t
309 std_ops(int32 op, ...)
310 {
311 	switch(op) {
312 		case B_MODULE_INIT:
313 			if(get_module(NET_CORE_MODULE_NAME, (module_info**)&core) != B_OK)
314 				return B_ERROR;
315 
316 			if(get_module(NET_ETHERNET_MODULE_NAME,
317 					(module_info**) &sEthernet) != B_OK) {
318 				put_module(NET_CORE_MODULE_NAME);
319 				return B_ERROR;
320 			}
321 
322 			set_max_linkhdr(PPPoE_HEADER_SIZE + ETHER_HDR_LEN);
323 			sDevices = new TemplateList<PPPoEDevice*>;
324 			sQueries = new TemplateList<pppoe_query*>;
325 			sEthernet->set_pppoe_receiver(pppoe_input);
326 
327 #if DEBUG
328 			dprintf("PPPoE: Registered PPPoE receiver.\n");
329 #endif
330 		return B_OK;
331 
332 		case B_MODULE_UNINIT:
333 			delete sDevices;
334 			sEthernet->unset_pppoe_receiver();
335 #if DEBUG
336 			dprintf("PPPoE: Unregistered PPPoE receiver.\n");
337 #endif
338 			put_module(NET_CORE_MODULE_NAME);
339 			put_module(NET_ETHERNET_MODULE_NAME);
340 		break;
341 
342 		default:
343 			return B_ERROR;
344 	}
345 
346 	return B_OK;
347 }
348 
349 
350 _EXPORT
351 module_info *modules[] = {
352 	(module_info*) &pppoe_module,
353 	NULL
354 };
355