source: src/router/pppd.new/pppd/plugins/rp-pppoe/plugin.c @ 17793

Last change on this file since 17793 was 17793, checked in by chris, 19 months ago

qos rewrite pppd/radius needs testing

File size: 14.2 KB
Line 
1/***********************************************************************
2*
3* plugin.c
4*
5* pppd plugin for kernel-mode PPPoE on Linux
6*
7* Copyright (C) 2001 by Roaring Penguin Software Inc., Michal Ostrowski
8* and Jamal Hadi Salim.
9*
10* Much code and many ideas derived from pppoe plugin by Michal
11* Ostrowski and Jamal Hadi Salim, which carries this copyright:
12*
13* Copyright 2000 Michal Ostrowski <mostrows@styx.uwaterloo.ca>,
14*                Jamal Hadi Salim <hadi@cyberus.ca>
15* Borrows heavily from the PPPoATM plugin by Mitchell Blank Jr.,
16* which is based in part on work from Jens Axboe and Paul Mackerras.
17*
18* This program is free software; you can redistribute it and/or
19* modify it under the terms of the GNU General Public License
20* as published by the Free Software Foundation; either version
21* 2 of the License, or (at your option) any later version.
22***********************************************************************/
23
24static char const RCSID[] =
25"$Id: plugin.c,v 1.15 2006/05/29 23:29:16 paulus Exp $";
26
27#define _GNU_SOURCE 1
28#include "pppoe.h"
29
30#include "pppd/pppd.h"
31#include "pppd/fsm.h"
32#include "pppd/lcp.h"
33#include "pppd/ipcp.h"
34#include "pppd/ccp.h"
35#include "pppd/pathnames.h"
36
37#include <linux/types.h>
38#include <syslog.h>
39#include <sys/ioctl.h>
40#include <sys/types.h>
41#include <sys/socket.h>
42#include <sys/stat.h>
43#include <string.h>
44#include <stdlib.h>
45#include <errno.h>
46#include <unistd.h>
47#include <fcntl.h>
48#include <signal.h>
49#include <net/ethernet.h>
50#include <net/if_arp.h>
51#include "ppp_defs.h"
52#include "if_ppp.h"
53#include "if_pppox.h"
54
55#define _PATH_ETHOPT         _ROOT_PATH "/etc/ppp/options."
56
57char pppd_version[] = VERSION;
58
59/* From sys-linux.c in pppd -- MUST FIX THIS! */
60extern int new_style_driver;
61
62char *pppd_pppoe_service = NULL;
63static char *acName = NULL;
64static char *existingSession = NULL;
65static int printACNames = 0;
66
67static int PPPoEDevnameHook(char *cmd, char **argv, int doit);
68static option_t Options[] = {
69    { "device name", o_wild, (void *) &PPPoEDevnameHook,
70      "PPPoE device name",
71      OPT_DEVNAM | OPT_PRIVFIX | OPT_NOARG  | OPT_A2STRVAL | OPT_STATIC,
72      devnam},
73    { "rp_pppoe_service", o_string, &pppd_pppoe_service,
74      "Desired PPPoE service name" },
75    { "rp_pppoe_ac",      o_string, &acName,
76      "Desired PPPoE access concentrator name" },
77    { "rp_pppoe_sess",    o_string, &existingSession,
78      "Attach to existing session (sessid:macaddr)" },
79    { "rp_pppoe_verbose", o_int, &printACNames,
80      "Be verbose about discovered access concentrators"},
81    { NULL }
82};
83
84static PPPoEConnection *conn = NULL;
85
86/**********************************************************************
87 * %FUNCTION: PPPOEInitDevice
88 * %ARGUMENTS:
89 * None
90 * %RETURNS:
91 *
92 * %DESCRIPTION:
93 * Initializes PPPoE device.
94 ***********************************************************************/
95static int
96PPPOEInitDevice(void)
97{
98    conn = malloc(sizeof(PPPoEConnection));
99    if (!conn) {
100        fatal("Could not allocate memory for PPPoE session");
101    }
102    memset(conn, 0, sizeof(PPPoEConnection));
103    if (acName) {
104        SET_STRING(conn->acName, acName);
105    }
106    if (pppd_pppoe_service) {
107        SET_STRING(conn->serviceName, pppd_pppoe_service);
108    }
109    SET_STRING(conn->ifName, devnam);
110    conn->discoverySocket = -1;
111    conn->sessionSocket = -1;
112    conn->useHostUniq = 1;
113    conn->printACNames = printACNames;
114    return 1;
115}
116
117#ifdef HAVE_AQOS
118int stricmp(char *a,char *b)
119{
120int l1 = strlen(a);
121int l2 = strlen(b);
122if (l2>l1)
123    return -1;
124int i;
125int i2=0;
126for (i=0;i<l2;i++)
127    {
128    if (i2==strlen(b))
129        {
130        return -1;
131        break;
132        }
133    if (a[i]==' ')
134        continue;
135    if (b[i2]==' ')
136        {
137        i2++;
138        i--;
139        continue;
140        }
141    if (toupper(a[i])!=toupper(b[i2]))
142        return -1;
143    i2++;
144    }
145return 0;
146}
147extern void add_usermac( char *mac, int idx, char *upstream,
148                         char *downstream, char *lanstream );
149extern char *nvram_safe_get(const char *name);
150
151int addrule(char *mac, char *upstream, char *downstream)
152{
153    char *qos_mac = nvram_safe_get( "svqos_macs" );
154    int ret = 0;
155    if (strlen(qos_mac)>0)
156    {
157    char *newqos = malloc(strlen(qos_mac)*2);
158    memset(newqos,0,strlen(qos_mac)*2);
159    char level[32], level2[32], level3[32], data[32], type[32];
160    do
161    {
162        if( sscanf( qos_mac, "%31s %31s %31s %31s %31s |", data, level, level2 , type, level3) < 4 )
163            break;
164        if (!stricmp(data,mac) && !strcmp(level,upstream) && !strcmp(level2,downstream))
165            {
166            sprintf(newqos,"%s %s %s %s %s %s |",newqos,data,level,level2,type,level3);     
167            ret |=1;
168            }
169            else
170            {
171            if (!stricmp(data,mac))
172            {
173            ret |=2;
174            }
175            sprintf(newqos,"%s %s %s %s %s %s |",newqos,data,upstream,downstream,"pppd",level3);           
176//          sprintf(newqos,"%s %s %s %s %s |",newqos,data,level,level2,type);       
177            }
178    }
179    while( ( qos_mac = strpbrk( ++qos_mac, "|" ) ) && qos_mac++ );
180    nvram_set("svqos_macs",newqos);
181    free(newqos);
182    }else
183    {
184    char newqos[128];
185    sprintf(newqos,"%s %s %s %s %s |",mac,upstream,downstream,"pppd",level3);       
186    nvram_set("svqos_macs",newqos);   
187    }
188return ret;
189
190}
191#endif
192
193#ifndef MAC2STR
194#define MAC2STR(a) (a)[0], (a)[1], (a)[2], (a)[3], (a)[4], (a)[5]
195#define MACSTR "%02x:%02x:%02x:%02x:%02x:%02x"
196#endif
197
198/**********************************************************************
199 * %FUNCTION: PPPOEConnectDevice
200 * %ARGUMENTS:
201 * None
202 * %RETURNS:
203 * Non-negative if all goes well; -1 otherwise
204 * %DESCRIPTION:
205 * Connects PPPoE device.
206 ***********************************************************************/
207static int
208PPPOEConnectDevice(void)
209{
210    struct sockaddr_pppox sp;
211    static int qosidx=500;
212
213    strlcpy(ppp_devnam, devnam, sizeof(ppp_devnam));
214    if (existingSession) {
215        unsigned int mac[ETH_ALEN];
216        int i, ses;
217        if (sscanf(existingSession, "%d:%x:%x:%x:%x:%x:%x",
218                   &ses, &mac[0], &mac[1], &mac[2],
219                   &mac[3], &mac[4], &mac[5]) != 7) {
220            fatal("Illegal value for rp_pppoe_sess option");
221        }
222        conn->session = htons(ses);
223        for (i=0; i<ETH_ALEN; i++) {
224            conn->peerEth[i] = (unsigned char) mac[i];
225        }
226    } else {
227        discovery(conn);
228        if (conn->discoveryState != STATE_SESSION) {
229            error("Unable to complete PPPoE Discovery");
230            return -1;
231        }
232    }
233
234    /* Set PPPoE session-number for further consumption */
235    ppp_session_number = ntohs(conn->session);
236
237    /* Make the session socket */
238    conn->sessionSocket = socket(AF_PPPOX, SOCK_STREAM, PX_PROTO_OE);
239    if (conn->sessionSocket < 0) {
240        fatal("Failed to create PPPoE socket: %m");
241    }
242    sp.sa_family = AF_PPPOX;
243    sp.sa_protocol = PX_PROTO_OE;
244    sp.sa_addr.pppoe.sid = conn->session;
245    memcpy(sp.sa_addr.pppoe.dev, conn->ifName, IFNAMSIZ);
246    memcpy(sp.sa_addr.pppoe.remote, conn->peerEth, ETH_ALEN);
247
248    /* Set remote_number for ServPoET */
249    sprintf(remote_number, "%02X:%02X:%02X:%02X:%02X:%02X",
250            (unsigned) conn->peerEth[0],
251            (unsigned) conn->peerEth[1],
252            (unsigned) conn->peerEth[2],
253            (unsigned) conn->peerEth[3],
254            (unsigned) conn->peerEth[4],
255            (unsigned) conn->peerEth[5]);
256
257    if (connect(conn->sessionSocket, (struct sockaddr *) &sp,
258                sizeof(struct sockaddr_pppox)) < 0) {
259        fatal("Failed to connect PPPoE socket: %d %m", errno);
260        return -1;
261    }
262#ifdef HAVE_AQOS
263    if (bandwidthup!=0 && bandwidthdown!=0)
264        {
265        char uplevel[64];
266        char downlevel[64];
267        char mac[64];
268        sprintf(mac, MACSTR, MAC2STR(conn->myEth));
269        sprintf(uplevel,"%d",bandwidthup/1000);
270        sprintf(downlevel,"%d",bandwidthdown/1000);
271        fprintf(stderr,"use bandwidth down value to %d\n",bandwidthdown);
272        fprintf(stderr,"use bandwidth up value to %d\n",bandwidthdown);
273        int ret = addrule(mac,uplevel,downlevel);
274                    if (!ret)
275                        {
276                        qosidx+=2;
277                        if (qosidx>500)
278                            qosidx=0;
279                        add_usermac(mac, qosidx, uplevel,downlevel,"0" );
280                        }else if (ret>1)
281                        {
282                        system("startstop_f wshaper");
283                        }           
284        }
285#endif
286    return conn->sessionSocket;
287}
288
289/*static void
290PPPOESendConfig(int mtu,
291                u_int32_t asyncmap,
292                int pcomp,
293                int accomp)
294{
295    int sock;
296    struct ifreq ifr;
297
298    if (mtu > MAX_PPPOE_MTU) {
299        warn("Couldn't increase MTU to %d", mtu);
300        mtu = MAX_PPPOE_MTU;
301    }
302    sock = socket(AF_INET, SOCK_DGRAM, 0);
303    if (sock < 0) {
304        error("Couldn't create IP socket: %m");
305        return;
306    }
307    strlcpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
308//    info("set mtu rp-pppoe to %s (%d)\n",ifname,mtu);
309    ifr.ifr_mtu = mtu;
310    if (ioctl(sock, SIOCSIFMTU, &ifr) < 0) {
311        error("Couldn't set interface MTU to %d: %m", mtu);
312        return;
313    }
314    (void) close (sock);
315}
316*/
317static void
318PPPOERecvConfig(int mru,
319                u_int32_t asyncmap,
320                int pcomp,
321                int accomp)
322{
323#if 0 /* broken protocol, but no point harrassing the users I guess... */
324    if (mru > MAX_PPPOE_MTU)
325        warn("Couldn't increase MRU to %d", mru);
326#endif
327}
328
329/**********************************************************************
330 * %FUNCTION: PPPOEDisconnectDevice
331 * %ARGUMENTS:
332 * None
333 * %RETURNS:
334 * Nothing
335 * %DESCRIPTION:
336 * Disconnects PPPoE device
337 ***********************************************************************/
338static void
339PPPOEDisconnectDevice(void)
340{
341    struct sockaddr_pppox sp;
342
343    sp.sa_family = AF_PPPOX;
344    sp.sa_protocol = PX_PROTO_OE;
345    sp.sa_addr.pppoe.sid = 0;
346    memcpy(sp.sa_addr.pppoe.dev, conn->ifName, IFNAMSIZ);
347    memcpy(sp.sa_addr.pppoe.remote, conn->peerEth, ETH_ALEN);
348    if (connect(conn->sessionSocket, (struct sockaddr *) &sp,
349                sizeof(struct sockaddr_pppox)) < 0) {
350        fatal("Failed to disconnect PPPoE socket: %d %m", errno);
351        return;
352    }
353    close(conn->sessionSocket);
354    /* don't send PADT?? */
355    close(conn->discoverySocket);
356}
357
358static void
359PPPOEDeviceOptions(void)
360{
361    char buf[256];
362    snprintf(buf, 256, _PATH_ETHOPT "%s",devnam);
363    if(!options_from_file(buf, 0, 0, 1))
364        exit(EXIT_OPTION_ERROR);
365
366}
367
368struct channel pppoe_channel;
369
370/**********************************************************************
371 * %FUNCTION: PPPoEDevnameHook
372 * %ARGUMENTS:
373 * cmd -- the command (actually, the device name
374 * argv -- argument vector
375 * doit -- if non-zero, set device name.  Otherwise, just check if possible
376 * %RETURNS:
377 * 1 if we will handle this device; 0 otherwise.
378 * %DESCRIPTION:
379 * Checks if name is a valid interface name; if so, returns 1.  Also
380 * sets up devnam (string representation of device).
381 ***********************************************************************/
382static int
383PPPoEDevnameHook(char *cmd, char **argv, int doit)
384{
385    int r = 1;
386    int fd;
387    struct ifreq ifr;
388
389    /*
390     * Take any otherwise-unrecognized option as a possible device name,
391     * and test if it is the name of a network interface with a
392     * hardware address whose sa_family is ARPHRD_ETHER.
393     */
394    if (strlen(cmd) > 4 && !strncmp(cmd, "nic-", 4)) {
395        /* Strip off "nic-" */
396        cmd += 4;
397    }
398
399    /* Open a socket */
400    if ((fd = socket(PF_PACKET, SOCK_RAW, 0)) < 0) {
401        r = 0;
402    }
403
404    /* Try getting interface index */
405    if (r) {
406        strncpy(ifr.ifr_name, cmd, sizeof(ifr.ifr_name));
407        if (ioctl(fd, SIOCGIFINDEX, &ifr) < 0) {
408            r = 0;
409        } else {
410            if (ioctl(fd, SIOCGIFHWADDR, &ifr) < 0) {
411                r = 0;
412            } else {
413                if (ifr.ifr_hwaddr.sa_family != ARPHRD_ETHER) {
414                    if (doit)
415                        error("Interface %s not Ethernet", cmd);
416                    r = 0;
417                }
418            }
419        }
420    }
421
422    /* Close socket */
423    close(fd);
424    if (r && doit) {
425        strncpy(devnam, cmd, sizeof(devnam));
426        if (the_channel != &pppoe_channel) {
427
428            the_channel = &pppoe_channel;
429            modem = 0;
430
431            PPPOEInitDevice();
432        }
433        return 1;
434    }
435
436    return r;
437}
438
439/**********************************************************************
440 * %FUNCTION: plugin_init
441 * %ARGUMENTS:
442 * None
443 * %RETURNS:
444 * Nothing
445 * %DESCRIPTION:
446 * Initializes hooks for pppd plugin
447 ***********************************************************************/
448void
449plugin_init(void)
450{
451    if (!ppp_available() && !new_style_driver) {
452        fatal("Linux kernel does not support PPPoE -- are you running 2.4.x?");
453    }
454
455    add_options(Options);
456
457    info("RP-PPPoE plugin version %s compiled against pppd %s",
458         RP_VERSION, VERSION);
459}
460
461/**********************************************************************
462*%FUNCTION: fatalSys
463*%ARGUMENTS:
464* str -- error message
465*%RETURNS:
466* Nothing
467*%DESCRIPTION:
468* Prints a message plus the errno value to stderr and syslog and exits.
469***********************************************************************/
470void
471fatalSys(char const *str)
472{
473    char buf[1024];
474    int i = errno;
475    sprintf(buf, "%.256s: %.256s", str, strerror(i));
476    printErr(buf);
477    sprintf(buf, "RP-PPPoE: %.256s: %.256s", str, strerror(i));
478    sendPADT(conn, buf);
479    exit(1);
480}
481
482/**********************************************************************
483*%FUNCTION: rp_fatal
484*%ARGUMENTS:
485* str -- error message
486*%RETURNS:
487* Nothing
488*%DESCRIPTION:
489* Prints a message to stderr and syslog and exits.
490***********************************************************************/
491void
492rp_fatal(char const *str)
493{
494    char buf[1024];
495    printErr(str);
496    sprintf(buf, "RP-PPPoE: %.256s", str);
497    sendPADT(conn, buf);
498    exit(1);
499}
500/**********************************************************************
501*%FUNCTION: sysErr
502*%ARGUMENTS:
503* str -- error message
504*%RETURNS:
505* Nothing
506*%DESCRIPTION:
507* Prints a message plus the errno value to syslog.
508***********************************************************************/
509void
510sysErr(char const *str)
511{
512    rp_fatal(str);
513}
514
515void pppoe_check_options(void)
516{
517    lcp_allowoptions[0].neg_accompression = 0;
518    lcp_wantoptions[0].neg_accompression = 0;
519
520    lcp_allowoptions[0].neg_asyncmap = 0;
521    lcp_wantoptions[0].neg_asyncmap = 0;
522
523    lcp_allowoptions[0].neg_pcompression = 0;
524    lcp_wantoptions[0].neg_pcompression = 0;
525
526    if (lcp_allowoptions[0].mru > MAX_PPPOE_MTU)
527        lcp_allowoptions[0].mru = MAX_PPPOE_MTU;
528    if (lcp_wantoptions[0].mru > MAX_PPPOE_MTU)
529        lcp_wantoptions[0].mru = MAX_PPPOE_MTU;
530
531    ccp_allowoptions[0].deflate = 0;
532    ccp_wantoptions[0].deflate = 0;
533
534    ipcp_allowoptions[0].neg_vj = 0;
535    ipcp_wantoptions[0].neg_vj = 0;
536
537    ccp_allowoptions[0].bsd_compress = 0;
538    ccp_wantoptions[0].bsd_compress = 0;
539}
540
541struct channel pppoe_channel = {
542    options: Options,
543    process_extra_options: &PPPOEDeviceOptions,
544    check_options: pppoe_check_options,
545    connect: &PPPOEConnectDevice,
546    disconnect: &PPPOEDisconnectDevice,
547    establish_ppp: &generic_establish_ppp,
548    disestablish_ppp: &generic_disestablish_ppp,
549    send_config: NULL,
550    recv_config: &PPPOERecvConfig,
551    close: NULL,
552    cleanup: NULL
553};
Note: See TracBrowser for help on using the repository browser.