xref: /haiku/src/add-ons/kernel/network/ppp/shared/libppp/PPPManager.cpp (revision fe2557b6eb55be3c2d36e1ee396e0f10e41bd214)
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.
42 PPPManager::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.
70 PPPManager::~PPPManager()
71 {
72 	if (fFD >= 0)
73 		close(fFD);
74 }
75 
76 
77 //!	Sets the default interface.
78 bool
79 PPPManager::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.
110 BString
111 PPPManager::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.
121 bool
122 PPPManager::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.
141 status_t
142 PPPManager::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 */
162 status_t
163 PPPManager::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 */
198 status_t
199 PPPManager::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 */
221 ppp_interface_id
222 PPPManager::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 */
242 ppp_interface_id
243 PPPManager::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 */
281 bool
282 delete_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.
301 bool
302 PPPManager::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.
315 bool
316 PPPManager::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 */
340 ppp_interface_id*
341 PPPManager::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:%ld\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 		// printf("interfaces addr: %p\n, requestCount: %ld", interfaces, requestCount);
359 		*count = GetInterfaces(interfaces, requestCount, filter);
360 		if (*count <= 0) {
361 			printf("No interface, count second round:%ld\n", *count);
362 			delete interfaces;
363 			return NULL;
364 		}
365 
366 		if (*count < requestCount)
367 			break;
368 
369 		delete interfaces;
370 	}
371 
372 	return interfaces;
373 }
374 
375 
376 //!	Use \c Interfaces() instead of this method.
377 int32
378 PPPManager::GetInterfaces(ppp_interface_id *interfaces, int32 count,
379 	ppp_interface_filter filter) const
380 {
381 	ppp_get_interfaces_info info;
382 	info.interfaces = interfaces;
383 	info.count = count;
384 	info.filter = filter;
385 
386 	if (Control(PPPC_GET_INTERFACES, &info, sizeof(info)) != B_OK)
387 		return -1;
388 	else
389 		return info.resultCount;
390 }
391 
392 
393 //!	Returns the ID of the interface with the given settings on success.
394 ppp_interface_id
395 PPPManager::InterfaceWithSettings(const driver_settings *settings) const
396 {
397 	ppp_interface_description_info info;
398 	info.u.settings = settings;
399 	info.interface = PPP_UNDEFINED_INTERFACE_ID;
400 
401 	Control(PPPC_FIND_INTERFACE_WITH_SETTINGS, &info, sizeof(info));
402 
403 	return info.interface;
404 }
405 
406 
407 //!	Returns the ID of the interface with the given if_unit (interface unit).
408 ppp_interface_id
409 PPPManager::InterfaceWithUnit(int32 if_unit) const
410 {
411 	int32 count;
412 	ppp_interface_id *interfaces = Interfaces(&count, PPP_REGISTERED_INTERFACES);
413 
414 	if (!interfaces)
415 		return PPP_UNDEFINED_INTERFACE_ID;
416 
417 	ppp_interface_id id = PPP_UNDEFINED_INTERFACE_ID;
418 	PPPInterface interface;
419 	ppp_interface_info_t info;
420 
421 	for (int32 index = 0; index < count; index++) {
422 		interface.SetTo(interfaces[index]);
423 		if (interface.InitCheck() == B_OK && interface.GetInterfaceInfo(&info)
424 				&& info.info.if_unit == if_unit) {
425 			id = interface.ID();
426 			break;
427 		}
428 	}
429 
430 	delete interfaces;
431 
432 	return id;
433 }
434 
435 
436 //!	Returns the ID of the interface with the given name.
437 ppp_interface_id
438 PPPManager::InterfaceWithName(const char *name) const
439 {
440 	if (!name)
441 		return PPP_UNDEFINED_INTERFACE_ID;
442 
443 	int32 count;
444 	ppp_interface_id *interfaces = Interfaces(&count, PPP_REGISTERED_INTERFACES);
445 
446 	if (!interfaces || count <= 0) {
447 		printf("ERROR: Could not get ppp name:%s\n", name);
448 		return PPP_UNDEFINED_INTERFACE_ID;
449 	}
450 
451 	// printf("first ID:%ld count:%ld\n", interfaces[0], count);
452 
453 	ppp_interface_id id = PPP_UNDEFINED_INTERFACE_ID;
454 	PPPInterface interface;
455 	ppp_interface_info_t info;
456 	// printf("internal info is at: %p\n", &info);
457 
458 	for (int32 index = 0; index < count; index++) {
459 		interface.SetTo(interfaces[index]);
460 		if (interface.InitCheck() == B_OK && interface.GetInterfaceInfo(&info)
461 				&& strlen(info.info.name) > 0 && !strcasecmp(info.info.name, name)) {
462 			id = interface.ID();
463 			break;
464 		}
465 	}
466 
467 	delete interfaces;
468 
469 	if (id != PPP_UNDEFINED_INTERFACE_ID)
470 		return id;
471 	else if (!strncmp(name, "ppp", 3) && strlen(name) > 3 && isdigit(name[3]))
472 		return InterfaceWithUnit(atoi(name + 3));
473 	else if (isdigit(name[0]))
474 		return atoi(name);
475 	else
476 		return PPP_UNDEFINED_INTERFACE_ID;
477 }
478 
479 
480 //!	Returns the number of existing interfaces or a negative value on error.
481 bool
482 is_ppp_interface(const char* name)
483 {
484 	// size_t length = strlen(name);
485 	// if (length < 8)
486 	//	putchar('\t');
487 	// else
488 	//	printf("\n\t");
489 
490 	// get link level interface for this interface
491 
492 	BNetworkInterface interface(name);
493 	BNetworkAddress linkAddress;
494 	status_t status = interface.GetHardwareAddress(linkAddress);
495 	if (status == B_OK) {
496 		// const char *type = "unknown";
497 		switch (linkAddress.LinkLevelType()) {
498 			case IFT_ETHER:
499 				// type = "Ethernet";
500 				// printf("%s\n", type);
501 				break;
502 			case IFT_LOOP:
503 				// type = "Local Loopback";
504 				// printf("%s\n", type);
505 				break;
506 			case IFT_MODEM:
507 				// type = "Modem";
508 				// printf("%s\n", type);
509 				break;
510 			case IFT_PPP:
511 				// type = "PPP";
512 				// printf("%s\n", type);
513 				return true;
514 				break;
515 			default:
516 				// printf("%s\n", type);
517 				;
518 
519 		}
520 
521 	}
522 	return false;
523 }
524 
525 
526 //!	Returns the number of ppp interfaces
527 int32
528 count_ppp_interface(void)
529 {
530 	int32 count = 0;
531 
532 	// get a list of all ppp interfaces
533 	BNetworkRoster& roster = BNetworkRoster::Default();
534 
535 	BNetworkInterface interface;
536 	uint32 cookie = 0;
537 
538 	while (roster.GetNextInterface(&cookie, interface) == B_OK) {
539 		if (is_ppp_interface(interface.Name()))
540 			count++;
541 	}
542 
543 	return count;
544 }
545 
546 
547 //!	Returns the number of existing interfaces or a negative value on error.
548 int32
549 PPPManager::CountInterfaces(ppp_interface_filter filter) const
550 {
551 	// return Control(PPPC_COUNT_INTERFACES, &filter, sizeof(filter));
552 	return count_ppp_interface();
553 }
554 
555 
556 /*!	\brief Requests report messages from the PPP stack.
557 
558 	\param type The type of report.
559 	\param thread Receiver thread.
560 	\param flags Optional flags.
561 
562 	\return \c true on success \c false otherwise.
563 */
564 bool
565 PPPManager::EnableReports(ppp_report_type type, thread_id thread,
566 	int32 flags) const
567 {
568 	ppp_report_request request;
569 	request.type = type;
570 	request.thread = thread;
571 	request.flags = flags;
572 
573 	return Control(PPPC_ENABLE_REPORTS, &request, sizeof(request)) == B_OK;
574 }
575 
576 
577 /*!	\brief Removes thread from list of report requestors of this interface.
578 
579 	\param type The type of report.
580 	\param thread Receiver thread.
581 
582 	\return \c true on success \c false otherwise.
583 */
584 bool
585 PPPManager::DisableReports(ppp_report_type type, thread_id thread) const
586 {
587 	ppp_report_request request;
588 	request.type = type;
589 	request.thread = thread;
590 
591 	return Control(PPPC_DISABLE_REPORTS, &request, sizeof(request)) == B_OK;
592 }
593