xref: /haiku/src/add-ons/kernel/network/ppp/pppoe/pppoe.cpp (revision 1214ef1b2100f2b3299fc9d8d6142e46f70a4c3f)
1 /*
2  * Copyright 2003-2006, Waldemar Kornewald <wkornew@gmx.net>
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 	if(!sDevices->HasItem(device))
110 		sDevices->AddItem(device);
111 }
112 
113 
114 void
115 remove_device(PPPoEDevice *device)
116 {
117 	TRACE("PPPoE: remove_device()\n");
118 
119 	LockerHelper locker(sLock);
120 	sDevices->RemoveItem(device);
121 }
122 
123 
124 static
125 void
126 pppoe_input(struct mbuf *packet)
127 {
128 	if(!packet)
129 		return;
130 
131 	ifnet *sourceIfnet = packet->m_pkthdr.rcvif;
132 	complete_pppoe_header *header = mtod(packet, complete_pppoe_header*);
133 	PPPoEDevice *device;
134 	pppoe_query *query;
135 
136 	LockerHelper locker(sLock);
137 
138 	if(header->ethernetHeader.ether_type == ETHERTYPE_PPPOEDISC
139 			&& header->pppoeHeader.length <= PPPoE_QUERY_REPORT_SIZE) {
140 		for(int32 index = 0; index < sDevices->CountItems(); index++) {
141 			query = sQueries->ItemAt(index);
142 
143 			if(query && query->ethernetIfnet == sourceIfnet) {
144 				if(header->pppoeHeader.code == PADO) {
145 					DiscoveryPacket discovery(packet, ETHER_HDR_LEN);
146 					if(discovery.InitCheck() != B_OK) {
147 						ERROR("PPPoE: received corrupted discovery packet!\n");
148 						m_freem(packet);
149 						return;
150 					}
151 
152 					pppoe_tag *hostTag = discovery.TagWithType(HOST_UNIQ);
153 					if(hostTag && hostTag->length == 4
154 							&& *((uint32*)hostTag->data) == query->hostUniq) {
155 						SendQueryPacket(query, discovery);
156 						m_freem(packet);
157 						return;
158 					}
159 				}
160 			}
161 		}
162 	}
163 
164 	for(int32 index = 0; index < sDevices->CountItems(); index++) {
165 		device = sDevices->ItemAt(index);
166 
167 		if(device && device->EthernetIfnet() == sourceIfnet) {
168 			if(header->ethernetHeader.ether_type == ETHERTYPE_PPPOE
169 					&& header->pppoeHeader.sessionID == device->SessionID()) {
170 				TRACE("PPPoE: session packet\n");
171 				device->Receive(packet);
172 				return;
173 			} else if(header->ethernetHeader.ether_type == ETHERTYPE_PPPOEDISC
174 					&& header->pppoeHeader.code != PADI
175 					&& header->pppoeHeader.code != PADR
176 					&& !device->IsDown()) {
177 				TRACE("PPPoE: discovery packet\n");
178 				DiscoveryPacket discovery(packet, ETHER_HDR_LEN);
179 				if(discovery.InitCheck() != B_OK) {
180 					ERROR("PPPoE: received corrupted discovery packet!\n");
181 					m_freem(packet);
182 					return;
183 				}
184 
185 				pppoe_tag *tag = discovery.TagWithType(HOST_UNIQ);
186 				if(header->pppoeHeader.code == PADT || (tag && tag->length == 4
187 						&& *((uint32*)tag->data) == device->HostUniq())) {
188 					device->Receive(packet);
189 					return;
190 				}
191 			}
192 		}
193 	}
194 
195 	ERROR("PPPoE: No device found for packet from: %s\n", sourceIfnet->if_name);
196 	m_freem(packet);
197 }
198 
199 
200 static
201 bool
202 add_to(KPPPInterface& mainInterface, KPPPInterface *subInterface,
203 	driver_parameter *settings, ppp_module_key_type type)
204 {
205 	if(mainInterface.Mode() != PPP_CLIENT_MODE || type != PPP_DEVICE_KEY_TYPE)
206 		return B_ERROR;
207 
208 	PPPoEDevice *device;
209 	bool success;
210 	if(subInterface) {
211 		device = new PPPoEDevice(*subInterface, settings);
212 		success = subInterface->SetDevice(device);
213 	} else {
214 		device = new PPPoEDevice(mainInterface, settings);
215 		success = mainInterface.SetDevice(device);
216 	}
217 
218 	TRACE("PPPoE: add_to(): %s\n",
219 		success && device && device->InitCheck() == B_OK ? "OK" : "ERROR");
220 
221 	return success && device && device->InitCheck() == B_OK;
222 }
223 
224 
225 static
226 status_t
227 control(uint32 op, void *data, size_t length)
228 {
229 	switch(op) {
230 		case PPPoE_GET_INTERFACES: {
231 			int32 position = 0, count = 0;
232 			char *names = (char*) data;
233 
234 			ifnet *current = get_interfaces();
235 			for(; current; current = current->if_next) {
236 				if(current->if_type == IFT_ETHER && current->if_name) {
237 					if(position + strlen(current->if_name) + 1 > length)
238 						return B_NO_MEMORY;
239 
240 					strcpy(names + position, current->if_name);
241 					position += strlen(current->if_name);
242 					names[position++] = 0;
243 					++count;
244 				}
245 			}
246 
247 			return count;
248 		}
249 
250 		case PPPoE_QUERY_SERVICES: {
251 			// XXX: as all modules are loaded on-demand we must wait for the results
252 
253 			if(!data || length != sizeof(pppoe_query_request))
254 				return B_ERROR;
255 
256 			pppoe_query_request *request = (pppoe_query_request*) data;
257 
258 			pppoe_query query;
259 			query.ethernetIfnet = FindPPPoEInterface(request->interfaceName);
260 			if(!query.ethernetIfnet)
261 				return B_BAD_VALUE;
262 
263 			query.hostUniq = NewHostUniq();
264 			query.receiver = request->receiver;
265 
266 			sLock.Lock();
267 			sQueries->AddItem(&query);
268 			sLock.Unlock();
269 
270 			snooze(2000000);
271 				// wait two seconds for results
272 
273 			sLock.Lock();
274 			sQueries->RemoveItem(&query);
275 			sLock.Unlock();
276 		} break;
277 
278 		default:
279 			return B_ERROR;
280 	}
281 
282 	return B_OK;
283 }
284 
285 
286 static ppp_module_info pppoe_module = {
287 	{
288 		PPPoE_MODULE_NAME,
289 		0,
290 		std_ops
291 	},
292 	control,
293 	add_to
294 };
295 
296 
297 _EXPORT
298 status_t
299 std_ops(int32 op, ...)
300 {
301 	switch(op) {
302 		case B_MODULE_INIT:
303 			if(get_module(NET_CORE_MODULE_NAME, (module_info**)&core) != B_OK)
304 				return B_ERROR;
305 
306 			if(get_module(NET_ETHERNET_MODULE_NAME,
307 					(module_info**) &sEthernet) != B_OK) {
308 				put_module(NET_CORE_MODULE_NAME);
309 				return B_ERROR;
310 			}
311 
312 			set_max_linkhdr(2 + PPPoE_HEADER_SIZE + ETHER_HDR_LEN);
313 				// 2 bytes for PPP header
314 			sDevices = new TemplateList<PPPoEDevice*>;
315 			sQueries = new TemplateList<pppoe_query*>;
316 			sEthernet->set_pppoe_receiver(pppoe_input);
317 
318 			TRACE("PPPoE: Registered PPPoE receiver.\n");
319 		return B_OK;
320 
321 		case B_MODULE_UNINIT:
322 			delete sDevices;
323 			sEthernet->unset_pppoe_receiver();
324 			TRACE("PPPoE: Unregistered PPPoE receiver.\n");
325 			put_module(NET_CORE_MODULE_NAME);
326 			put_module(NET_ETHERNET_MODULE_NAME);
327 		break;
328 
329 		default:
330 			return B_ERROR;
331 	}
332 
333 	return B_OK;
334 }
335 
336 
337 _EXPORT
338 module_info *modules[] = {
339 	(module_info*) &pppoe_module,
340 	NULL
341 };
342