1/*
2 * Copyright (c) 2000 Apple Computer, Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
11 * file.
12 *
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
20 *
21 * @APPLE_LICENSE_HEADER_END@
22 */
23
24/* -----------------------------------------------------------------------------
25 *
26 *  Theory of operation :
27 *
28 *  plugin to add a generic socket support to pppd, instead of tty.
29 *
30----------------------------------------------------------------------------- */
31
32
33/* -----------------------------------------------------------------------------
34  Includes
35----------------------------------------------------------------------------- */
36
37#include <sys/param.h>
38#include <sys/types.h>
39#include <sys/wait.h>
40#include <sys/time.h>
41#include <sys/resource.h>
42#include <sys/socket.h>
43#include <sys/stat.h>
44#include <sys/socket.h>
45#include <netinet/in.h>
46#include <arpa/inet.h>
47#include <syslog.h>
48#include <sys/ioctl.h>
49#include <net/if.h>
50#include <sys/sys_domain.h>
51
52#include <stdio.h>
53#include <ctype.h>
54#include <stdlib.h>
55#include <string.h>
56#include <unistd.h>
57#include <signal.h>
58#include <errno.h>
59#include <fcntl.h>
60#include <syslog.h>
61#include <netdb.h>
62#include <pwd.h>
63#include <setjmp.h>
64
65#include <CoreFoundation/CFBundle.h>
66
67#define APPLE 1
68
69#include "../../../Family/ppp_defs.h"
70#include "../../../Family/if_ppp.h"
71#include "../../../Family/ppp_domain.h"
72#include "../PPPoE-extension/PPPoE.h"
73#include "../../../Helpers/pppd/pppd.h"
74#include "../../../Helpers/pppd/fsm.h"
75#include "../../../Helpers/pppd/lcp.h"
76
77
78/* -----------------------------------------------------------------------------
79 Definitions
80----------------------------------------------------------------------------- */
81
82#define MODE_CONNECT	"connect"
83#define MODE_LISTEN	"listen"
84#define MODE_ANSWER	"answer"
85
86#define PPPOE_NKE	"PPPoE.kext"
87#define PPPOE_NKE_ID	"com.apple.nke.pppoe"
88
89/* -----------------------------------------------------------------------------
90 Forward declarations
91----------------------------------------------------------------------------- */
92void pppoe_process_extra_options();
93void pppoe_check_options();
94int pppoe_connect(int *errorcode);
95void pppoe_disconnect();
96void pppoe_close();
97void pppoe_cleanup();
98int pppoe_establish_ppp(int);
99void pppoe_wait_input();
100void pppoe_disestablish_ppp(int);
101void pppoe_link_down(void *arg, uintptr_t p);
102
103static int pppoe_dial();
104static int pppoe_listen();
105static void closeall();
106static u_long load_kext(char *kext, int byBundleID);
107
108/* -----------------------------------------------------------------------------
109 PPP globals
110----------------------------------------------------------------------------- */
111
112
113static int 	sockfd = -1;			/* socket file descriptor */
114static CFBundleRef 	bundle = 0;		/* our bundle ref */
115
116/* option variables */
117static char 	*mode = MODE_CONNECT;		/* connect mode by default */
118static bool 	loopback = 0;			/* loop back mode */
119static bool 	noload = 0;			/* don't load the kernel extension */
120static char	*service = NULL; 		/* service selection to use */
121static char	*access_concentrator = NULL; 	/* access concentrator to connect to */
122static int	retrytimer = 0; 		/* retry timer (default is 3 seconds) */
123static int	connecttimer = 65; 		/* bump the connection timer from 20 to 65 seconds */
124static bool	linkdown = 0; 			/* flag set when we receive link down event */
125
126extern int kill_link;
127
128/* option descriptors */
129option_t pppoe_options[] = {
130    { "pppoeservicename", o_string, &service,
131      "PPPoE service to choose" },
132    { "pppoeacname", o_string, &access_concentrator,
133      "PPPoE Access Concentrator to connect to" },
134    { "pppoeloopback", o_bool, &loopback,
135      "Configure PPPoE in loopback mode, for single machine testing", 1 },
136    { "nopppoeload", o_bool, &noload,
137      "Don't try to load the PPPoE kernel extension", 1 },
138    { "pppoemode", o_string, &mode,
139      "Configure configuration mode [connect, listen, answer]" },
140    { "pppoeconnecttimer", o_int, &connecttimer,
141      "Connect timer for outgoing call (default 65 seconds)" },
142    { "pppoeretrytimer", o_int, &retrytimer,
143      "Retry timer for outgoing call (default 3 seconds)" },
144    { NULL }
145};
146
147
148/* -----------------------------------------------------------------------------
149plugin entry point, called by pppd
150----------------------------------------------------------------------------- */
151int start(CFBundleRef ref)
152{
153
154    bundle = ref;
155    CFRetain(bundle);
156
157    // hookup our socket handlers
158    bzero(the_channel, sizeof(struct channel));
159    the_channel->options = pppoe_options;
160    the_channel->process_extra_options = pppoe_process_extra_options;
161    the_channel->wait_input = pppoe_wait_input;
162    the_channel->check_options = pppoe_check_options;
163    the_channel->connect = pppoe_connect;
164    the_channel->disconnect = pppoe_disconnect;
165    the_channel->cleanup = pppoe_cleanup;
166    the_channel->close = pppoe_close;
167    the_channel->establish_ppp = pppoe_establish_ppp;
168    the_channel->disestablish_ppp = pppoe_disestablish_ppp;
169    // use the default config functions
170    the_channel->send_config = generic_send_config;
171    the_channel->recv_config = generic_recv_config;
172
173    add_notifier(&link_down_notifier, pppoe_link_down, 0);
174    return 0;
175}
176
177/* -----------------------------------------------------------------------------
178----------------------------------------------------------------------------- */
179void pppoe_link_down(void *arg, uintptr_t p)
180{
181    linkdown = 1;
182}
183
184/* -----------------------------------------------------------------------------
185work out which device we are using and read its options file
186----------------------------------------------------------------------------- */
187void pppoe_process_extra_options()
188{
189
190    if (!device)
191        device = "en0";
192
193    if (!strcmp(mode, MODE_ANSWER)) {
194        // make sure we get a file descriptor > 2 so that pppd can detach and close 0,1,2
195        sockfd = dup(0);
196    }
197}
198
199/* -----------------------------------------------------------------------------
200do consistency checks on the options we were given
201----------------------------------------------------------------------------- */
202void pppoe_check_options()
203{
204}
205
206/* -----------------------------------------------------------------------------
207called back everytime we go out of select, and data needs to be read
208the hook is called and has a chance to get data out of its file descriptor
209in the case of PPPoE, we are not supposed to get data on the socket
210if our socket gets awaken, that's because is has been closed
211----------------------------------------------------------------------------- */
212void pppoe_wait_input()
213{
214
215    if (sockfd != -1 && is_ready_fd(sockfd)) {
216        // looks like we have been disconnected...
217        // the status is updated only if link is not already down
218        if (linkdown == 0) {
219            notice("PPPoE hangup");
220            status = EXIT_HANGUP;
221        }
222        remove_fd(sockfd);
223        hungup = 1;
224        lcp_lowerdown(0);	/* PPPoE link is no longer available */
225        link_terminated(0);
226    }
227}
228
229/* -----------------------------------------------------------------------------
230get the socket ready to start doing PPP.
231That is, open the socket and run the connector
232----------------------------------------------------------------------------- */
233int pppoe_connect(int *errorcode)
234{
235    char 	dev[32], name[MAXPATHLEN];
236    int 	err = 0, len, s;
237    CFURLRef	url;
238    struct ifreq 	ifr;
239
240	*errorcode = 0;
241
242    snprintf(dev, sizeof(dev), "socket[%d:%d]", PF_PPP, PPPPROTO_PPPOE);
243    strlcpy(ppp_devnam, dev, sizeof(ppp_devnam));
244
245    hungup = 0;
246    kill_link = 0;
247    linkdown = 0;
248
249	err = -1;
250    s = socket(PF_SYSTEM, SOCK_RAW, SYSPROTO_EVENT);
251    if (s >= 0) {
252
253        len = strlen(device);
254        if (len <= sizeof(ifr.ifr_name)) {
255
256            bzero(&ifr, sizeof(ifr));
257            bcopy(device, ifr.ifr_name, len);
258            if (ioctl(s, SIOCGIFFLAGS, (caddr_t) &ifr) >= 0) {
259                // ensure that the device is UP
260                ifr.ifr_flags |= IFF_UP;
261                if (ioctl(s, SIOCSIFFLAGS, (caddr_t) &ifr) >= 0)
262                    err = 0;
263            }
264        }
265
266        close(s);
267    }
268	if (err) {
269		error("PPPoE cannot use interface '%s'.", device);
270		status = EXIT_OPEN_FAILED;
271		return -1;
272	}
273
274    if (strcmp(mode, MODE_ANSWER)) {
275        /* open the socket */
276        sockfd = socket(PF_PPP, SOCK_DGRAM, PPPPROTO_PPPOE);
277        if (sockfd < 0) {
278            if (!noload) {
279                if ((url = CFBundleCopyBundleURL(bundle))) {
280                    name[0] = 0;
281                    CFURLGetFileSystemRepresentation(url, 0, (UInt8 *)name, MAXPATHLEN - 1);
282                    CFRelease(url);
283                    strlcat(name, "/", sizeof(name));
284                    if ((url = CFBundleCopyBuiltInPlugInsURL(bundle))) {
285                        CFURLGetFileSystemRepresentation(url, 0, (UInt8 *)(name + strlen(name)),
286                            MAXPATHLEN - strlen(name) - strlen(PPPOE_NKE) - 1);
287                        CFRelease(url);
288                        strlcat(name, "/", sizeof(name));
289                        strlcat(name, PPPOE_NKE, sizeof(name));
290#if !TARGET_OS_EMBEDDED // This file is not built for Embedded
291                        if (!load_kext(name, 0))
292#else
293                        if (!load_kext(PPPOE_NKE_ID, 1))
294#endif
295                            sockfd = socket(PF_PPP, SOCK_DGRAM, PPPPROTO_PPPOE);
296                    }
297                }
298            }
299            if (sockfd < 0) {
300                error("Failed to open PPPoE socket: %m");
301                status = EXIT_OPEN_FAILED;
302                return -1;
303            }
304        }
305    }
306
307    if (loopback || debug) {
308        u_int32_t 	flags;
309        flags = (loopback ? PPPOE_FLAG_LOOPBACK : 0)
310            + ((kdebugflag & 1) ? PPPOE_FLAG_DEBUG : 0);
311        if (setsockopt(sockfd, PPPPROTO_PPPOE, PPPOE_OPT_FLAGS, &flags, 4)) {
312            error("PPPoE can't set PPPoE flags...\n");
313            return errno;
314        }
315        if (loopback)
316            notice("PPPoE loopback activated...\n");
317    }
318
319    if (connecttimer) {
320        u_int16_t 	timer = connecttimer;
321        if (setsockopt(sockfd, PPPPROTO_PPPOE, PPPOE_OPT_CONNECT_TIMER, &timer, 2)) {
322            error("PPPoE can't set PPPoE connect timer...\n");
323            return errno;
324        }
325    }
326
327    if (retrytimer) {
328        u_int16_t 	timer = retrytimer;
329        if (setsockopt(sockfd, PPPPROTO_PPPOE, PPPOE_OPT_RETRY_TIMER, &timer, 2)) {
330            error("PPPoE can't set PPPoE retry timer...\n");
331            return errno;
332        }
333    }
334
335    if (setsockopt(sockfd, PPPPROTO_PPPOE, PPPOE_OPT_INTERFACE, device, strlen(device))) {
336        error("PPPoE can't specify interface...\n");
337        return errno;
338    }
339
340    if (!strcmp(mode, MODE_ANSWER)) {
341        // nothing to do
342    }
343    else if (!strcmp(mode, MODE_LISTEN)) {
344        err = pppoe_listen();
345    }
346    else if (!strcmp(mode, MODE_CONNECT)) {
347        err = pppoe_dial();
348    }
349    else
350        fatal("PPPoE incorrect mode : '%s'", mode ? mode : "");
351
352    if (err) {
353        if (err != -2) {
354            if (err != -1)
355                devstatus = err;
356            status = EXIT_CONNECT_FAILED;
357        }
358        return -1;
359    }
360
361    return sockfd;
362}
363
364/* -----------------------------------------------------------------------------
365run the disconnector connector
366----------------------------------------------------------------------------- */
367void pppoe_disconnect()
368{
369    notice("PPPoE disconnecting...\n");
370
371    if (shutdown(sockfd, SHUT_RDWR) < 0) {
372        error("PPPoE disconnection failed, error = %d.\n", errno);
373        return;
374    }
375
376    notice("PPPoE disconnected\n");
377}
378
379/* -----------------------------------------------------------------------------
380close the socket descriptors
381----------------------------------------------------------------------------- */
382void pppoe_close()
383{
384	if (sockfd >= 0) {
385		close(sockfd);
386		sockfd = -1;
387	}
388}
389
390/* -----------------------------------------------------------------------------
391clean up before quitting
392----------------------------------------------------------------------------- */
393void pppoe_cleanup()
394{
395    pppoe_close();
396}
397
398/* -----------------------------------------------------------------------------
399establish the socket as a ppp link
400----------------------------------------------------------------------------- */
401int pppoe_establish_ppp(int fd)
402{
403    int x, new_fd;
404
405    if (ioctl(fd, PPPIOCATTACH, &x) < 0) {
406        error("Couldn't attach socket to the link layer: %m");
407        return -1;
408    }
409
410    new_fd = generic_establish_ppp(fd, NULL);
411    if (new_fd == -1)
412        return -1;
413
414    /* add our pppoe socket to the select */
415    add_fd(fd);
416
417    return new_fd;
418}
419
420/* -----------------------------------------------------------------------------
421disestablish the socket as a ppp link
422----------------------------------------------------------------------------- */
423void pppoe_disestablish_ppp(int fd)
424{
425    int 	x;
426
427    remove_fd(fd);
428
429    if (ioctl(fd, PPPIOCDETACH, &x) < 0)
430        error("Couldn't detach socket from link layer: %m");
431
432    generic_disestablish_ppp(fd);
433}
434
435/* -----------------------------------------------------------------------------
436----------------------------------------------------------------------------- */
437int pppoe_dial()
438{
439    struct sockaddr_pppoe 	addr;
440    u_short 			len, i;
441	unsigned char ac_address[ETHER_ADDR_LEN];
442	char ac_string[ETHER_ADDR_LEN * 3];
443	socklen_t ac_len = ETHER_ADDR_LEN;
444	int err;
445
446    // if the specific pppoe option are not used, try to see
447    // if the remote address generic field can be decomposed
448    // in the form of service_name\access_concentrator
449    if (!service && !access_concentrator && remoteaddress) {
450	len = strlen(remoteaddress);
451	for (i = 0; i < len; i++)
452            if (remoteaddress[i] == '\\')
453                break;
454        if ((service = malloc(i + 1))) {
455            strncpy(service, remoteaddress, i);
456            service[i] = 0;
457        }
458        if (i < len) {
459            if ((access_concentrator = malloc(len - i)))
460                strlcpy(access_concentrator, &remoteaddress[i + 1], len - i);
461        }
462    }
463
464    notice("PPPoE connecting to service '%s' [access concentrator '%s']...\n",
465            service ? service : "",
466            access_concentrator ? access_concentrator : "");
467
468    bzero(&addr, sizeof(addr));
469    addr.ppp.ppp_len = sizeof(struct sockaddr_pppoe);
470    addr.ppp.ppp_family = AF_PPP;
471    addr.ppp.ppp_proto = PPPPROTO_PPPOE;
472    if (access_concentrator)
473        strncpy(addr.pppoe_ac_name, access_concentrator, sizeof(addr.pppoe_ac_name));
474    if (service)
475        strncpy(addr.pppoe_service, service, sizeof(addr.pppoe_service));
476    if (connect(sockfd, (struct sockaddr *)&addr, sizeof(struct sockaddr_pppoe)) < 0) {
477        if (errno == EINTR)
478            return -2; // user cancelled
479        error("PPPoE connection failed, %m");
480        switch (errno) {
481            case EHOSTUNREACH:
482                if ((service && service[0]) && (access_concentrator && access_concentrator[0]))
483                    return EXIT_PPPoE_NOACSERVICE;
484                else if (service && service[0])
485                    return EXIT_PPPoE_NOSERVICE;
486                else if (access_concentrator && access_concentrator[0])
487                    return EXIT_PPPoE_NOAC;
488                return EXIT_PPPoE_NOSERVER;
489
490            case ECONNREFUSED:
491                return EXIT_PPPoE_CONNREFUSED;
492
493            case ENXIO:
494                // Ethernet interface is detached
495                // fake a cancel to get a consistent
496                // error message with the HANGUP case
497                status = EXIT_HANGUP;
498                return -2;
499
500        }
501        return -1;
502    }
503
504	/* build network signature from the Access Concentrator address */
505	err = getsockopt(sockfd, PPPPROTO_PPPOE, PPPOE_OPT_PEER_ENETADDR, ac_address, &ac_len);
506	if (err == -1) {
507		warning("PPPoE cannot retrieve access concentrator address, %m");
508	}
509	else {
510		snprintf(ac_string, sizeof(ac_string), "%02X:%02X:%2X:%02X:%02X:%02X", ac_address[0], ac_address[1], ac_address[2], ac_address[3], ac_address[4], ac_address[5]);
511		set_network_signature("PPPoE.AccessConcentratorAddress", ac_string, 0, 0);
512	}
513
514    notice("PPPoE connection established.");
515    return 0;
516}
517
518
519/* -----------------------------------------------------------------------------
520----------------------------------------------------------------------------- */
521int pppoe_listen()
522{
523    struct sockaddr_pppoe 	addr;
524    socklen_t				len;
525	int						fd;
526
527    notice("PPPoE listening on service '%s' [access concentrator '%s']...\n",
528            service ? service : "",
529            access_concentrator ? access_concentrator : "");
530
531    bzero(&addr, sizeof(addr));
532    addr.ppp.ppp_len = sizeof(struct sockaddr_pppoe);
533    addr.ppp.ppp_family = AF_PPP;
534    addr.ppp.ppp_proto = PPPPROTO_PPPOE;
535    if (access_concentrator)
536        strncpy(addr.pppoe_ac_name, access_concentrator, sizeof(addr.pppoe_ac_name));
537    if (service)
538        strncpy(addr.pppoe_service, service, sizeof(addr.pppoe_service));
539    if (bind(sockfd, (struct sockaddr *)&addr, sizeof(struct sockaddr_pppoe)) < 0) {
540        error("PPPoE bind failed, %m");
541        return -1;
542    }
543
544    if (listen(sockfd, 10) < 0) {
545        error("PPPoE listen failed, %m");
546        return -1;
547    }
548
549    len = sizeof(addr);
550    fd = accept(sockfd, (struct sockaddr *) &addr, &len);
551    if (fd < 0) {
552        error("PPPoE accept failed, %m");
553        return -1;
554    }
555
556    close(sockfd);	// close the socket used for listening
557    sockfd = fd;	// use the accepted socket instead of
558
559    notice("PPPoE connection established in incoming call.");
560    return 0;
561}
562
563/* -----------------------------------------------------------------------------
564----------------------------------------------------------------------------- */
565void closeall()
566{
567    int i;
568
569    for (i = getdtablesize() - 1; i >= 0; i--) close(i);
570    open("/dev/null", O_RDWR, 0);
571    dup(0);
572    dup(0);
573    return;
574}
575
576/* -----------------------------------------------------------------------------
577----------------------------------------------------------------------------- */
578u_long load_kext(char *kext, int byBundleID)
579{
580    int pid;
581
582    if ((pid = fork()) < 0)
583        return 1;
584
585    if (pid == 0) {
586        closeall();
587        // PPP kernel extension not loaded, try load it...
588		if (byBundleID)
589			execle("/sbin/kextload", "kextload", "-b", kext, (char *)0, (char *)0);
590		else
591			execle("/sbin/kextload", "kextload", kext, (char *)0, (char *)0);
592        exit(1);
593    }
594
595    while (waitpid(pid, 0, 0) < 0) {
596        if (errno == EINTR)
597            continue;
598       return 1;
599    }
600    return 0;
601}
602
603