1/*
2 * Copyright 2003-2007, Waldemar Kornewald <wkornew@gmx.net>
3 * Distributed under the terms of the MIT License.
4 */
5
6/*!	\class PPPManager
7	\brief Allows controlling the PPP stack.
8
9	This class can be used for creating and deleting interfaces. It has methods for
10	requesting PPP stack report messages (e.g.: about newly created interfaces).
11*/
12
13#include "PPPManager.h"
14#include "PPPInterface.h"
15#include "MessageDriverSettingsUtils.h"
16
17#include <Directory.h>
18#include <File.h>
19#include <Message.h>
20
21#include <cstring>
22#include <cstdlib>
23#include <cstdio>
24#include <cctype>
25#include <settings_tools.h>
26#include <unistd.h>
27
28#include <net/if.h>
29
30#include <net/if_media.h>
31#include <net/if_types.h>
32
33#include <Message.h>
34#include <Messenger.h>
35#include <NetworkDevice.h>
36#include <NetworkInterface.h>
37#include <NetworkRoster.h>
38
39#include <NetServer.h>
40
41//!	Constructor. Does nothing special.
42PPPManager::PPPManager()
43{
44	// fFD = open(get_stack_driver_path(), O_RDWR);
45	int family = AF_INET;
46
47	fFD = socket(family, SOCK_DGRAM, 0);
48
49	// FileDescriptorCloser closer(socket);
50
51	// ifaliasreq request;
52	// strlcpy(request.ifra_name, name, IF_NAMESIZE);
53	// request.ifra_index = address.Index();
54	// request.ifra_flags = address.Flags();
55
56	// memcpy(&request.ifra_addr, &address.Address().SockAddr(),
57	//	address.Address().Length());
58	// memcpy(&request.ifra_mask, &address.Mask().SockAddr(),
59	// 	address.Mask().Length());
60	// memcpy(&request.ifra_broadaddr, &address.Broadcast().SockAddr(),
61	//	address.Broadcast().Length());
62
63	// if (ioctl(socket, option, &request, sizeof(struct ifaliasreq)) < 0)
64	//	return errno;
65
66}
67
68
69//!	Destructor.
70PPPManager::~PPPManager()
71{
72	if (fFD >= 0)
73		close(fFD);
74}
75
76
77//!	Sets the default interface.
78bool
79PPPManager::SetDefaultInterface(const BString name)
80{
81	// load current settings and replace value of "default" with <name>
82	BMessage settings;
83	if (!ReadMessageDriverSettings("ptpnet.settings", &settings))
84		settings.MakeEmpty();
85
86	BMessage parameter;
87	int32 index = 0;
88	if (FindMessageParameter("default", settings, &parameter, &index))
89		settings.RemoveData(MDSU_PARAMETERS, index);
90
91	parameter.MakeEmpty();
92	if (name != "") {
93		parameter.AddString(MDSU_NAME, "default");
94		parameter.AddString(MDSU_VALUES, name);
95		settings.AddMessage(MDSU_PARAMETERS, &parameter);
96	}
97
98	BFile file(PTP_SETTINGS_PATH, B_WRITE_ONLY | B_CREATE_FILE | B_ERASE_FILE);
99	if (file.InitCheck() != B_OK)
100		return false;
101
102	if (WriteMessageDriverSettings(file, settings))
103		return true;
104	else
105		return false;
106}
107
108
109//!	Returns the name of the default interface.
110BString
111PPPManager::DefaultInterface()
112{
113	void *handle = load_driver_settings("ptpnet.settings");
114	BString name = get_driver_parameter(handle, "default", NULL, NULL);
115	unload_driver_settings(handle);
116	return name;
117}
118
119
120//!	Sets the given BDirectory to the settings folder.
121bool
122PPPManager::GetSettingsDirectory(BDirectory *settingsDirectory)
123{
124	if (settingsDirectory) {
125		BDirectory settings(PTP_INTERFACE_SETTINGS_PATH);
126		if (settings.InitCheck() != B_OK) {
127			create_directory(PTP_INTERFACE_SETTINGS_PATH, 0750);
128			settings.SetTo(PTP_INTERFACE_SETTINGS_PATH);
129			if (settings.InitCheck() != B_OK)
130				return false;
131		}
132
133		*settingsDirectory = settings;
134	}
135
136	return true;
137}
138
139
140//!	Returns \c B_OK if created successfully and \c B_ERROR otherwise.
141status_t
142PPPManager::InitCheck() const
143{
144	if (fFD < 0)
145		return B_ERROR;
146	else
147		return B_OK;
148}
149
150
151/*!	\brief Offers an ioctl()-like interface to all functions of the PPP stack.
152
153	\param op Any value of ppp_control_ops.
154	\param data Some ops require you to pass a structure or other data using this
155		argument.
156	\param length Make sure this value is correct (e.g.: size of structure).
157
158	If you cannot find the method that fits your needs or if you want to have direct
159	access to the complete set of  PPP functions you should use this method. All
160	other methods call \c Control(), i.e., they are wrappers around this method.
161*/
162status_t
163PPPManager::Control(uint32 op, void *data, size_t length) const
164{
165	if (InitCheck() != B_OK)
166		return B_ERROR;
167
168	control_net_module_args args;
169	sprintf(args.ifr_name, "%s", "ppp1");
170	args.name = PPP_INTERFACE_MODULE_NAME;
171	args.op = op;
172	args.data = data;
173	args.length = length;
174
175	return ioctl(fFD, NET_STACK_CONTROL_NET_MODULE, &args);
176}
177
178
179/*!	\brief Controls a specific PPP module.
180
181	Use this method if you want to access a PPP module. The PPP stack will load it,
182	then call its \c control() function (if it is exported), and finally unload
183	the module.
184
185	\param name The module name.
186	\param op The private control op.
187	\param data Some ops require you to pass a structure or other data using this
188		argument.
189	\param length Make sure this value is correct (e.g.: size of structure).
190
191	\return
192		- \c B_NAME_NOT_FOUND: The module could not be found.
193		- \c B_ERROR: Some error occured.
194		- The module's return value.
195
196	\sa ppp_module_info::control()
197*/
198status_t
199PPPManager::ControlModule(const char *name, uint32 op, void *data,
200	size_t length) const
201{
202	if (!name)
203		return B_ERROR;
204
205	control_net_module_args args;
206	sprintf(args.ifr_name, "%s", "ppp1");
207	args.name = name;
208	args.op = op;
209	args.data = data;
210	args.length = length;
211	return Control(PPPC_CONTROL_MODULE, &args, sizeof(args));
212}
213
214
215/*!	\brief Creates a nameless interface with the given settings.
216
217	Please use \c CreateInterfaceWithName() instead of this method.
218
219	\return the new interface's ID or \c PPP_UNDEFINED_INTERFACE_ID on failure.
220*/
221ppp_interface_id
222PPPManager::CreateInterface(const driver_settings *settings) const
223{
224	ppp_interface_description_info info;
225	info.u.settings = settings;
226
227	if (Control(PPPC_CREATE_INTERFACE, &info, sizeof(info)) != B_OK)
228		return PPP_UNDEFINED_INTERFACE_ID;
229	else
230		return info.interface;
231}
232
233
234/*!	\brief Creates an interface with the given name.
235
236	If the interface already exists its ID will be returned.
237
238	\param name The PPP interface description file's name.
239
240	\return the new interface's ID or \c PPP_UNDEFINED_INTERFACE_ID on failure.
241*/
242ppp_interface_id
243PPPManager::CreateInterfaceWithName(const char *name) const
244{
245	ppp_interface_id ID = InterfaceWithName(name);
246
247	if (ID != PPP_UNDEFINED_INTERFACE_ID)
248		return ID;
249
250	BNetworkInterface interface(name);
251	if (!interface.Exists()) {
252		// the interface does not exist yet, we have to add it first
253		BNetworkRoster& roster = BNetworkRoster::Default();
254
255		status_t status = roster.AddInterface(interface);
256		if (status != B_OK) {
257			fprintf(stderr, "PPPManager::CreateInterfaceWithName: Could not add interface: %s\n",
258				strerror(status));
259			return PPP_UNDEFINED_INTERFACE_ID;
260		}
261
262		return InterfaceWithName(name);
263	}
264
265	return PPP_UNDEFINED_INTERFACE_ID;
266
267//	ppp_interface_description_info info;
268//	info.u.name = name;
269//
270//	if (Control(PPPC_CREATE_INTERFACE_WITH_NAME, &info, sizeof(info)) != B_OK)
271//		return PPP_UNDEFINED_INTERFACE_ID;
272//	else
273//		return info.interface;
274
275}
276
277
278/*!	It will remove the complete interface with all
279	of its addresses.
280*/
281bool
282delete_interface(const char* name)
283{
284	BNetworkInterface interface(name);
285
286	// Delete interface
287	BNetworkRoster& roster = BNetworkRoster::Default();
288
289	status_t status = roster.RemoveInterface(interface);
290	if (status != B_OK) {
291		fprintf(stderr, "delete_interface: Could not delete interface %s\n",
292			name);
293		return false;
294	}
295
296	return true;
297}
298
299
300//!	Deletes the interface with the given \a name.
301bool
302PPPManager::DeleteInterface(const char* name) const
303{
304	ppp_interface_id ID = InterfaceWithName(name);
305
306	if (ID == PPP_UNDEFINED_INTERFACE_ID)
307		return false;
308
309	return delete_interface(name);
310
311}
312
313
314//!	Deletes the interface with the given \a ID.
315bool
316PPPManager::DeleteInterface(ppp_interface_id ID) const
317{
318	if (Control(PPPC_DELETE_INTERFACE, &ID, sizeof(ID)) != B_OK)
319		return false;
320	else
321		return true;
322}
323
324
325/*!	\brief Returns all interface IDs matching a certain filter rule.
326
327	ATTENTION: You are responsible for deleting (via \c delete) the returned data!\n
328	Use this if you want to iterate over all interfaces. It returns an array of all
329	interface IDs.\c
330	You can specify a filter rule that can be either of:
331		- \c PPP_REGISTERED_INTERFACES (default): Only visible interfaces.
332		- \c PPP_UNREGISTERED_INTERFACES: Only invisible interfaces.
333		- \c PPP_ALL_INTERFACES: All (visible and invisible) interfaces.
334
335	\param count The number of IDs in the returned array is stored here.
336	\param filter The filter rule.
337
338	\return an array of interface IDs or \c NULL on failure.
339*/
340ppp_interface_id*
341PPPManager::Interfaces(int32 *count,
342	ppp_interface_filter filter) const
343{
344	int32 requestCount;
345	ppp_interface_id *interfaces;
346
347	// loop until we get all interfaces
348	while (true) {
349		requestCount = *count = CountInterfaces(filter);
350		if (*count <= 0) {
351			printf("No interface, count, first round: %" B_PRId32 "\n", *count);
352			return NULL;
353		}
354
355		requestCount += 10;
356		// request some more interfaces in case some are added in the mean time
357		interfaces = new ppp_interface_id[requestCount];
358
359		//printf("interfaces addr: %p\n, requestCount: %ld", interfaces,
360		//	requestCount);
361		*count = GetInterfaces(interfaces, requestCount, filter);
362		if (*count <= 0) {
363			printf("No interface, count second round: %" B_PRId32 "\n", *count);
364			delete interfaces;
365			return NULL;
366		}
367
368		if (*count < requestCount)
369			break;
370
371		delete interfaces;
372	}
373
374	return interfaces;
375}
376
377
378//!	Use \c Interfaces() instead of this method.
379int32
380PPPManager::GetInterfaces(ppp_interface_id *interfaces, int32 count,
381	ppp_interface_filter filter) const
382{
383	ppp_get_interfaces_info info;
384	info.interfaces = interfaces;
385	info.count = count;
386	info.filter = filter;
387
388	if (Control(PPPC_GET_INTERFACES, &info, sizeof(info)) != B_OK)
389		return -1;
390	else
391		return info.resultCount;
392}
393
394
395//!	Returns the ID of the interface with the given settings on success.
396ppp_interface_id
397PPPManager::InterfaceWithSettings(const driver_settings *settings) const
398{
399	ppp_interface_description_info info;
400	info.u.settings = settings;
401	info.interface = PPP_UNDEFINED_INTERFACE_ID;
402
403	Control(PPPC_FIND_INTERFACE_WITH_SETTINGS, &info, sizeof(info));
404
405	return info.interface;
406}
407
408
409//!	Returns the ID of the interface with the given if_unit (interface unit).
410ppp_interface_id
411PPPManager::InterfaceWithUnit(int32 if_unit) const
412{
413	int32 count;
414	ppp_interface_id *interfaces = Interfaces(&count, PPP_REGISTERED_INTERFACES);
415
416	if (!interfaces)
417		return PPP_UNDEFINED_INTERFACE_ID;
418
419	ppp_interface_id id = PPP_UNDEFINED_INTERFACE_ID;
420	PPPInterface interface;
421	ppp_interface_info_t info;
422
423	for (int32 index = 0; index < count; index++) {
424		interface.SetTo(interfaces[index]);
425		if (interface.InitCheck() == B_OK && interface.GetInterfaceInfo(&info)
426				&& info.info.if_unit == if_unit) {
427			id = interface.ID();
428			break;
429		}
430	}
431
432	delete interfaces;
433
434	return id;
435}
436
437
438//!	Returns the ID of the interface with the given name.
439ppp_interface_id
440PPPManager::InterfaceWithName(const char *name) const
441{
442	if (!name)
443		return PPP_UNDEFINED_INTERFACE_ID;
444
445	int32 count;
446	ppp_interface_id *interfaces = Interfaces(&count, PPP_REGISTERED_INTERFACES);
447
448	if (!interfaces || count <= 0) {
449		printf("ERROR: Could not get ppp name:%s\n", name);
450		return PPP_UNDEFINED_INTERFACE_ID;
451	}
452
453	// printf("first ID:%ld count:%ld\n", interfaces[0], count);
454
455	ppp_interface_id id = PPP_UNDEFINED_INTERFACE_ID;
456	PPPInterface interface;
457	ppp_interface_info_t info;
458	// printf("internal info is at: %p\n", &info);
459
460	for (int32 index = 0; index < count; index++) {
461		interface.SetTo(interfaces[index]);
462		if (interface.InitCheck() == B_OK && interface.GetInterfaceInfo(&info)
463				&& strlen(info.info.name) > 0 && !strcasecmp(info.info.name, name)) {
464			id = interface.ID();
465			break;
466		}
467	}
468
469	delete interfaces;
470
471	if (id != PPP_UNDEFINED_INTERFACE_ID)
472		return id;
473	else if (!strncmp(name, "ppp", 3) && strlen(name) > 3 && isdigit(name[3]))
474		return InterfaceWithUnit(atoi(name + 3));
475	else if (isdigit(name[0]))
476		return atoi(name);
477	else
478		return PPP_UNDEFINED_INTERFACE_ID;
479}
480
481
482//!	Returns the number of existing interfaces or a negative value on error.
483bool
484is_ppp_interface(const char* name)
485{
486	// size_t length = strlen(name);
487	// if (length < 8)
488	//	putchar('\t');
489	// else
490	//	printf("\n\t");
491
492	// get link level interface for this interface
493
494	BNetworkInterface interface(name);
495	BNetworkAddress linkAddress;
496	status_t status = interface.GetHardwareAddress(linkAddress);
497	if (status == B_OK) {
498		// const char *type = "unknown";
499		switch (linkAddress.LinkLevelType()) {
500			case IFT_ETHER:
501				// type = "Ethernet";
502				// printf("%s\n", type);
503				break;
504			case IFT_LOOP:
505				// type = "Local Loopback";
506				// printf("%s\n", type);
507				break;
508			case IFT_MODEM:
509				// type = "Modem";
510				// printf("%s\n", type);
511				break;
512			case IFT_PPP:
513				// type = "PPP";
514				// printf("%s\n", type);
515				return true;
516				break;
517			default:
518				// printf("%s\n", type);
519				;
520
521		}
522
523	}
524	return false;
525}
526
527
528//!	Returns the number of ppp interfaces
529int32
530count_ppp_interface(void)
531{
532	int32 count = 0;
533
534	// get a list of all ppp interfaces
535	BNetworkRoster& roster = BNetworkRoster::Default();
536
537	BNetworkInterface interface;
538	uint32 cookie = 0;
539
540	while (roster.GetNextInterface(&cookie, interface) == B_OK) {
541		if (is_ppp_interface(interface.Name()))
542			count++;
543	}
544
545	return count;
546}
547
548
549//!	Returns the number of existing interfaces or a negative value on error.
550int32
551PPPManager::CountInterfaces(ppp_interface_filter filter) const
552{
553	// return Control(PPPC_COUNT_INTERFACES, &filter, sizeof(filter));
554	return count_ppp_interface();
555}
556
557
558/*!	\brief Requests report messages from the PPP stack.
559
560	\param type The type of report.
561	\param thread Receiver thread.
562	\param flags Optional flags.
563
564	\return \c true on success \c false otherwise.
565*/
566bool
567PPPManager::EnableReports(ppp_report_type type, thread_id thread,
568	int32 flags) const
569{
570	ppp_report_request request;
571	request.type = type;
572	request.thread = thread;
573	request.flags = flags;
574
575	return Control(PPPC_ENABLE_REPORTS, &request, sizeof(request)) == B_OK;
576}
577
578
579/*!	\brief Removes thread from list of report requestors of this interface.
580
581	\param type The type of report.
582	\param thread Receiver thread.
583
584	\return \c true on success \c false otherwise.
585*/
586bool
587PPPManager::DisableReports(ppp_report_type type, thread_id thread) const
588{
589	ppp_report_request request;
590	request.type = type;
591	request.thread = thread;
592
593	return Control(PPPC_DISABLE_REPORTS, &request, sizeof(request)) == B_OK;
594}
595