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