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.
PPPManager()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.
~PPPManager()70 PPPManager::~PPPManager()
71 {
72 if (fFD >= 0)
73 close(fFD);
74 }
75
76
77 //! Sets the default interface.
78 bool
SetDefaultInterface(const BString name)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, ¶meter, &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, ¶meter);
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
DefaultInterface()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
GetSettingsDirectory(BDirectory * settingsDirectory)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
InitCheck() const142 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
Control(uint32 op,void * data,size_t length) const163 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
ControlModule(const char * name,uint32 op,void * data,size_t length) const199 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
CreateInterface(const driver_settings * settings) const222 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
CreateInterfaceWithName(const char * name) const243 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
delete_interface(const char * name)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
DeleteInterface(const char * name) const302 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
DeleteInterface(ppp_interface_id ID) const316 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*
Interfaces(int32 * count,ppp_interface_filter filter) const341 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: %" 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.
379 int32
GetInterfaces(ppp_interface_id * interfaces,int32 count,ppp_interface_filter filter) const380 PPPManager::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.
396 ppp_interface_id
InterfaceWithSettings(const driver_settings * settings) const397 PPPManager::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).
410 ppp_interface_id
InterfaceWithUnit(int32 if_unit) const411 PPPManager::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.
439 ppp_interface_id
InterfaceWithName(const char * name) const440 PPPManager::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.
483 bool
is_ppp_interface(const char * name)484 is_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
529 int32
count_ppp_interface(void)530 count_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.
550 int32
CountInterfaces(ppp_interface_filter filter) const551 PPPManager::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 */
566 bool
EnableReports(ppp_report_type type,thread_id thread,int32 flags) const567 PPPManager::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 */
586 bool
DisableReports(ppp_report_type type,thread_id thread) const587 PPPManager::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