分析pptpd程序中關於執行pptpd和pppd程序的部分源代碼

火星人 @ 2014-03-04 , reply:0


分析pptpd程序中關於執行pptpd和pppd程序的部分源代碼

最近使用pptpd部署vpn server,為了更好的了解pptpd程序,我特意看了它的源代碼,並做了一些記錄,這是其中一篇記錄,有不正確的地方請各位指正,謝謝!
我對linux,對c語言的學習都還不夠,寫的東西里會有一些不清楚或者錯誤的地方,希望各位指點,也希望和大家一起討論。



整個pptpd程序的源頭應該是pptpd.c的main()函數。

在pptpd.c的main()函數的420行調用了pptp_manager()函數:
        /* manage connections until SIGTERM */
        pptp_manager(argc, argv);
這個函數在pptpmanager.c中。

-------------------------------------

pptpmanager.c中第310行開始的代碼,是為處理客戶端連接而生成的子進程調用connectCall()函數:
#ifndef HAVE_FORK
                                switch (ctrl_pid = vfork()) {
#else
                                switch (ctrl_pid = fork()) {
#endif

                                ......

                                case 0:        /* child */   /* 第310行 */
                                        close(hostSocket);
                                        if (pptp_debug)
                                                syslog(LOG_DEBUG, "MGR: Launching " PPTP_CTRL_BIN " to handle client");
#if !defined(PPPD_IP_ALLOC)
                                        connectCall(clientSocket, firstOpen);
#else
                                        connectCall(clientSocket, 0);
#endif
                                        _exit(1);



-------------------------------------

在pptpmanager.c的453行開始的代碼,感覺這裡是pptpd里真正為客戶端分配ip地址的地方,這部分代碼屬於connectCall()函數:

#if !defined(PPPD_IP_ALLOC)
extern char localIP;
        /* localIP數組應該存放了所有本地ip,每個數組元素中存放一個ip,
           總的元素個數就是允許的最大連接數 */
extern char remoteIP;

/* for now, this contains all relavant info about a call
* we are assuming that the remote and local IP's never change
* and are set at startup.
*/
struct callArray {
        pid_t pid;
        char pppRemote;
        char pppLocal;
};

        ......

/* Author: Kevin Thayer
* this routine sets up the arguments for the call handler and calls it. */

static void connectCall(int clientSocket, int clientNumber)
{

#define NUM2ARRAY(array, num) snprintf(array, sizeof(array), "%d", num)
        
        char *ctrl_argv;        /* arguments for launching 'pptpctrl' binary */

        int pptpctrl_argc = 0;        /* count the number of arguments sent to pptpctrl */

        ........

#if PPPD_IP_ALLOC    /* 第453行 */
        /* no local or remote address to specify */
        ctrl_argv = "0";
        ctrl_argv = "0";
#else
        /* specify local & remote addresses for this call */
        ctrl_argv = "1";
        ctrl_argv = localIP;  
        ctrl_argv = "1";
        ctrl_argv = remoteIP;

        ........

        /* ok, args are setup: invoke the call handler */
        execve(PPTP_CTRL_BIN, ctrl_argv, environ);
              /*本函數最後,調用pptpd程序,把ctrl_argv和environ做為參數 */
              /* defaults.h中定義:#define PPTP_CTRL_BIN       SBINDIR "/pptpctrl" */
        syslog(LOG_ERR, "MGR: Failed to exec " PPTP_CTRL_BIN "!");
        _exit(1);
}
#endif

上面connectCall函數中的execve()實際調用了pptpctrl程序,這個程序的源代碼應該是pptpctrl.c,看來又再次返回到pptpctrl。這個程序接收ctrl_argv和environ兩個參數,其中ctrl_argv數組中包括了localIP和remoteIP兩個變數,這正是我感興趣的部分。
execve()調用成功后不會返回,所以該語句後面跟的是出錯以後的處理。

-------------------------------------

pptp_handle_ctrl_connection()函數被pptpctrl.c的main()函數調用,相關代碼在main()的尾部:

int main(int argc, char **argv)   /* main()一開始就定義了很多變數 */
{
        char pppLocal;                /* local IP to pass to pppd */
        char pppRemote;                /* remote IP address to pass to pppd */
        struct sockaddr_in addr;        /* client address */
        socklen_t addrlen;
        int arg = 1;
        int flags;
        struct in_addr inetaddrs;
        char *pppaddrs = { pppLocal, pppRemote };

        ......

        /* be ready for a grisly death */
        sigpipe_create();
        sigpipe_assign(SIGTERM);
        NOTE_VALUE(PAC, call_id_pair, htons(-1));
        NOTE_VALUE(PNS, call_id_pair, htons(-1));

        syslog(LOG_INFO, "CTRL: Client %s control connection started", inet_ntoa(addr.sin_addr));
        pptp_handle_ctrl_connection(pppaddrs, inetaddrs);   /* 調用這個函數,處理來自客戶端的連接 */
             /* 上面這個函數里就調用了pppaddrs數組和inetaddrs結構,說明在此以前就有賦值 */

        syslog(LOG_DEBUG, "CTRL: Reaping child PPP[%i]", pppfork);
        if (pppfork > 0)  /* 結束用於處理客戶端連接的子進程 */
                waitpid(pppfork, NULL, 0);
        syslog(LOG_INFO, "CTRL: Client %s control connection finished", inet_ntoa(addr.sin_addr));

        bail(0);                /* NORETURN */
        return 1;                /* make gcc happy */
}

-------------------------------------

在pptpctrl.c的383行,是pptp_handle_ctrl_connection()函數,它調用了startCall()函數,相關代碼:
/*
* pptp_handle_ctrl_connection
*
* 1. read a packet (should be start_ctrl_conn_rqst)
* 2. reply to packet (send a start_ctrl_conn_rply)
* 3. proceed with GRE and CTRL connections
*
* args: pppaddrs - ppp local and remote addresses (strings)
*       inetaddrs - local and client socket address
* retn: 0 success, -1 failure
*/
static void pptp_handle_ctrl_connection(char **pppaddrs, struct in_addr *inetaddrs)
{
    ......

    /* start the call, by launching pppd */
    syslog(LOG_INFO, "CTRL: Starting call (launching pppd, opening GRE)");
    pty_fd = startCall(pppaddrs, inetaddrs);

    ......

}

-------------------------------------

同一文件下的startCall()函數,pptpd為客戶端連接建立一個子進程,在這個子進程里調用pppd程序(第634行):
<!--more-->
/*
* startCall
*
* Launches PPPD for the call.
*
* args:        pppaddrs - local/remote IPs or "" for either/both if none
* retn:        pty file descriptor
*
*/
static int startCall(char **pppaddrs, struct in_addr *inetaddrs)
{

        ......

        /* Launch the PPPD  */
#ifndef HAVE_FORK
        switch(pppfork=vfork()){
#else
        switch(pppfork=fork()){
#endif
        case -1:        /* fork() error */
                syslog(LOG_ERR, "CTRL: Error forking to exec pppd");
                _exit(1);

        case 0:                /* child */
                /* 子進程中調用pppd */

                ......

                launch_pppd(pppaddrs, inetaddrs);  /* launch_pppd函數用於調用pppd */
                syslog(LOG_ERR, "CTRL: PPPD launch failed! (launch_pppd did not fork)");
                _exit(1);
        }
        
        close(tty_fd);
        return pty_fd;
}


-------------------------------------

同一文件655行是launch_pppd函數:
/*
* launch_pppd
*
* Launches the PPP daemon. The PPP daemon is responsible for assigning the
* PPTP client its IP address.. These values are assigned via the command
* line.
*
* Add return of connected ppp interface
*
* retn: 0 on success, -1 on failure.
*
*/
static void launch_pppd(char **pppaddrs, struct in_addr *inetaddrs)
{

        ......

#else

        /* 以下這部分應該是pptpd調用pppd的時候執行的代碼 */
        /* options for 'normal' pppd */
        
        pppd_argv = "local";

        /* If a pppd option file is specified, use it
         * if not, pppd will default to /etc/ppp/options
         */
        if (*pppdxfig) {
                pppd_argv = "file";
                pppd_argv = pppdxfig;
        }
       
        /* If a speed has been specified, use it
         * if not, use "smart" default (defaults.h)
         */
        if (*speed) {
                pppd_argv = speed;
        } else {
                pppd_argv = PPP_SPEED_DEFAULT;
        }

        if (pptpctrl_debug) {
                /* 以下兩個if語句在系統日誌文件里輸出localip和remoteip,
                   在/var/log/messages里可以查到這些輸出 */
                if (*pppaddrs)
                        syslog(LOG_DEBUG, "CTRL (PPPD Launcher): local address = %s", pppaddrs);
                if (*pppaddrs)
                        syslog(LOG_DEBUG, "CTRL (PPPD Launcher): remote address = %s", pppaddrs);
        }
       
        if (*pppaddrs || *pppaddrs) {
                char pppInterfaceIPs;
                sprintf(pppInterfaceIPs, "%s:%s", pppaddrs, pppaddrs);
                pppd_argv = pppInterfaceIPs;
        }
#endif

        ......

        if (pptp_logwtmp) {                    /* logwtmp相關的代碼 */
                 pppd_argv = "plugin";
                 pppd_argv = "/usr/lib/pptpd/pptpd-logwtmp.so";
                 pppd_argv = "pptpd-original-ip";
                 pppd_argv = inet_ntoa(inetaddrs);
        }

        /* argv arrays must always be NULL terminated */
        pppd_argv = NULL;
        /* make sure SIGCHLD is unblocked, pppd does not expect it */
        sigfillset(&sigs);
        sigprocmask(SIG_UNBLOCK, &sigs, NULL);
        /* run pppd now */
        execvp(pppd_argv, pppd_argv);   /* 就是這一句調用了pppd程序 */
        /* execvp() failed */
        syslog(LOG_ERR,
               "CTRL (PPPD Launcher): Failed to launch PPP daemon. %s",
               strerror(errno));
}

-------------------------------------

關於上面函數中尾部的execvp(pppd_argv, pppd_argv)語句:
launch_pppd()函數里定義:
char *pppd_argv;

然後函數中對14個數組元素進行定義,其中第0個元素存放了pppd的執行程序:
pppd_argv = ppp_binary;
從程序代碼中還可以看到這個數組的其他元素都被賦了值,這些值應該都是pppd程序的參數。

這裡的ppp_binary在本文的頭部定義:
static char *ppp_binary = PPP_BINARY;

-------------------------------------

PPP_BINARY變數在configure.in里有定義:
if test "$BSDUSER_PPP" = "yes"; then
  AC_DEFINE(PPP_BINARY, "/usr/sbin/ppp")
else
  if test "$SLIRP" = "yes"; then
    AC_DEFINE(PPP_BINARY, "/bin/slirp")
  else
    AC_DEFINE(PPP_BINARY, "/usr/sbin/pppd")
  fi
fi

[ 本帖最後由 sailer_sh 於 2006-2-10 21:51 編輯 ]
《解決方案》

PPPD中pap認證部分的代碼

PPPD中pap認證部分的代碼:


upap.h里的結構,感覺客戶端用戶名和密碼應該保存在這裡面,但是還要找是誰把用戶名/密碼保存進去,以及誰調用了這個結構:
<!--more-->
/*
* Each interface is described by upap structure.
*/
typedef struct upap_state {
    int us_unit;                /* Interface unit number */
    char *us_user;                /* User */
    int us_userlen;                /* User length */
    char *us_passwd;                /* Password */
    int us_passwdlen;                /* Password length */
    int us_clientstate;                /* Client state */
    int us_serverstate;                /* Server state */
    u_char us_id;                /* Current id */
    int us_timeouttime;                /* Timeout (seconds) for auth-req retrans. */
    int us_transmits;                /* Number of auth-reqs sent */
    int us_maxtransmits;        /* Maximum number of auth-reqs to send */
    int us_reqtimeout;                /* Time to wait for auth-req from peer */
} upap_state;

auth.c的check_passwd()函數,實際證明下來客戶端傳來的用戶名和密碼被做為這個函數的參數,即auser和apasswd。
/* check_passwd - Check the user name and passwd against the PAP secrets
* file.  If requested, also check against the system password database,
* and login the user if OK.
*
* returns:
*        UPAP_AUTHNAK: Authentication failed.
*        UPAP_AUTHACK: Authentication succeeded.
* In either case, msg points to an appropriate message. */
int check_passwd(unit, auser, userlen, apasswd, passwdlen, msg)
    int unit;
    char *auser;
    int userlen;
    char *apasswd;
    int passwdlen;
    char **msg;
{
    int ret;
    char *filename;
    FILE *f;
    struct wordlist *addrs = NULL, *opts = NULL;
    char passwd, user;   /* 從這兩個數組的定義可以猜到pppd可以接收的用戶名和密碼的最大長度都是256個位元組*/
    char secret;
    static int attempts = 0;

    /* 下面把用戶名和密碼複製到user/passwd數組 */
    /*
     * Make copies of apasswd and auser, then null-terminate them.
     * If there are unprintable characters in the password, make
     * them visible.
     */
    slprintf(passwd, sizeof(passwd), "%.*v", passwdlen, apasswd);
    slprintf(user, sizeof(user), "%.*v", userlen, auser);
    *msg = "";

    /* 接下來打開pap認證的密碼文件/etc/ppp/pap-secrets,這個文件在_PATH_UPAPFILE中定義 */
    /*
     * Open the file of pap secrets and scan for a suitable secret
     * for authenticating this user.
     */
    filename = _PATH_UPAPFILE;
    addrs = opts = NULL;
    ret = UPAP_AUTHNAK;
    f = fopen(filename, "r");
    if (f == NULL) {
        error("Can't open PAP password file %s: %m", filename);
    } else {
        check_access(f, filename);  /* 檢查文件是否可讀 */
        if (scan_authfile(f, user, our_name, secret, &addrs, &opts, filename, 0) < 0) {  /* 查找文件中是否有某用戶的密碼 */
            warn("no PAP secret found for %s", user);
        } else {   /* 如果密碼欄位設置為"@login「,那麼就在login database中檢查密碼,這個login database就是系統的用戶/密碼文件 */
            /*
             * If the secret is "@login", it means to check
             * the password against the login database.
             */
            int login_secret = strcmp(secret, "@login") == 0;
            ret = UPAP_AUTHACK;
            if (uselogin || login_secret) {
                /* login option or secret is @login */
                if ((ret = plogin(user, passwd, msg)) == UPAP_AUTHACK)
                    used_login = 1;
            }
            if (secret != 0 && !login_secret) {
                /* password given in pap-secrets - must match */
                if ((cryptpap || strcmp(passwd, secret) != 0)   /* 如果密碼不匹配,就設置ret的值為UPAP_AUTHNAK */
                    && strcmp(crypt(passwd, secret), secret) != 0)
                    ret = UPAP_AUTHNAK;
            }
        }
        fclose(f);
    }

    if (ret == UPAP_AUTHNAK) {  /* ret的值為UPAP_AUTHNAK表示登錄不成功 */
        if (**msg == 0)
            *msg = "Login incorrect";
        /*
         * XXX can we ever get here more than once??
         * Frustrate passwd stealer programs.
         * Allow 10 tries, but start backing off after 3 (stolen from login).
         * On 10'th, drop the connection.
         */
        if (attempts++ >= 10) {   /* 登錄次數超過10次 */
            warn("%d LOGIN FAILURES ON %s, %s", attempts, devnam, user);
            lcp_close(unit, "login failed");
        }
        if (attempts > 3)
            sleep((u_int) (attempts - 3) * 5);
        if (opts != NULL)
            free_wordlist(opts);

    } else {   /* ret的值不為UPAP_AUTHNAK表示登錄成功 */
        attempts = 0;                        /* Reset count */
        if (**msg == 0)
            *msg = "Login ok";
        set_allowed_addrs(unit, addrs, opts);
    }

    if (addrs != NULL)
        free_wordlist(addrs);
    BZERO(passwd, sizeof(passwd));  /* 清空來自客戶端的密碼 */
    BZERO(secret, sizeof(secret));  /* 清空來自配置文件的密碼 */

    return ret;
}

upap.c的upap_rauthreq()函數調用了上面的check_passwd()函數,客戶端用戶名和密碼存放在指針inp里:
/* upap_rauth - Receive Authenticate. */
static void
upap_rauthreq(u, inp, id, len)
    upap_state *u;
    u_char *inp;
    int id;
    int len;
{
    u_char ruserlen, rpasswdlen;
    char *ruser, *rpasswd;
    char rhostname;
    int retcode;
    char *msg;
    int msglen;

    ......

    GETCHAR(ruserlen, inp);
/*  我覺得很奇怪,ruserlen是u_char,即無符號字元型,但它實際存放的是用戶名的長度,為什麼不把它設置為int型呢?不知道有誰能解決我的問題?
    GETCHAR()的原型:
        #define GETCHAR(c, cp) { \
                (c) = *(cp)++; \
        }
    從程序這裡判斷,給ruserlen賦的值正好是inp指向內存區域的第一個地址,這個地址存放的是一個控制字元,在putty里顯示為"^B", 不知道為什麼這個值就是用戶名長度,我還沒想通。
*/
    ruser = (char *) inp;   /* 指針ruser指向指針inp指向的地址的第二個位元組開始的部分,就是用戶名 */
    INCPTR(ruserlen, inp);  /* 指針inp向前移,移的數量就是用戶名的長度,相當於把指針移到用戶名後面一個位元組 */
    GETCHAR(rpasswdlen, inp);  /* 和ruserlen一樣,給rpasswdlen賦值,這個值就是密碼的長度,也看不懂。。。 */
    if (len < rpasswdlen) {    /* 如果len < rpasswdlen,那麼這個數據是不正常的 */
        UPAPDEBUG(("pap_rauth: rcvd short packet."));
        return;
    }
    rpasswd = (char *) inp;  /* rpasswd指向客戶端密碼 */

    /* Check the username and password given.   */
    /* 調用check_passwd函數檢查用戶名和密碼,ruser是用戶名,rpasswd是密碼 */
    retcode = check_passwd(u->us_unit, ruser, ruserlen, rpasswd,
                           rpasswdlen, &msg);
    BZERO(rpasswd, rpasswdlen);
調試下來發現參數inp指向的字元串里存放著用戶名和密碼,例如用戶名是st,密碼是abcdef,它的存放方式是:
^Bst^Fabcdef
^B和^F是兩個控制字元,我覺得它們是用來分隔用戶名和密碼的。
參數len是兩個控制字元(各佔一個位元組)加用戶名及密碼的長度的總和。


同文件的upap_input()調用了上面的upap_rauthreq():
/*
* upap_input - Input UPAP packet.
*/
static void
upap_input(unit, inpacket, l)
    int unit;
    u_char *inpacket;
    int l;
{
    upap_state *u = &upap;
    u_char *inp;
    u_char code, id;
    int len;
    /*
     * Parse header (code, id and length).
     * If packet too short, drop it.
     */
    inp = inpacket;     /* 把inpacket賦給inp,用戶名/密碼保存在inpacket中 */

    ......

    switch (code) {
    case UPAP_AUTHREQ:
        upap_rauthreq(u, inp, id, len);   /* 調用upap_rauthreq()函數,並且傳遞了inp參數 */
        break;


沒有一個函數顯式的調用upap_input()函數,應該是在某個地方通過下面的結構來調用它。

-----------------------------------------------------------

所有和pap認證相關的函數都放在同文件的結構里:
struct protent pap_protent = {
    PPP_PAP,
    upap_init,
    upap_input,  /* 這個函數接收了客戶端用戶名和密碼 */
    upap_protrej,
    upap_lowerup,
    upap_lowerdown,
    NULL,
    NULL,
    upap_printpkt,
    NULL,
    1,
    "PAP",
    NULL,
    pap_option_list,
    NULL,
    NULL,
    NULL
};

main.c定義了protocols結構數組:
/*
* PPP Data Link Layer "protocol" table.
* One entry per supported protocol.
* The last entry must be NULL.
*/
struct protent *protocols[] = {
    &lcp_protent,
    &pap_protent,    /* pap認證相關結構 */
    &chap_protent,
#ifdef CBCP_SUPPORT
    &cbcp_protent,
#endif
    &ipcp_protent,
#ifdef INET6
    &ipv6cp_protent,
#endif
    &ccp_protent,
    &ecp_protent,
#ifdef IPX_CHANGE
    &ipxcp_protent,
#endif
#ifdef AT_CHANGE
    &atcp_protent,
#endif
    &eap_protent,
    NULL
};

接下來,在main()里把protocols結構的值賦給protp結構數組:
    /*
     * Initialize each protocol.
     */
    for (i = 0; (protp = protocols) != NULL; ++i)
        (*protp->init)(0);

pppd.h中定義protent結構:
/*
* The following struct gives the addresses of procedures to call
* for a particular protocol.
*/
struct protent {
    u_short protocol;                /* PPP protocol number */
    /* Initialization procedure */
    void (*init) __P((int unit));
    /* Process a received packet */
    void (*input) __P((int unit, u_char *pkt, int len));
    /* Process a received protocol-reject */
    void (*protrej) __P((int unit));
    /* Lower layer has come up */
    void (*lowerup) __P((int unit));
    /* Lower layer has gone down */
    void (*lowerdown) __P((int unit));
    /* Open the protocol */
    void (*open) __P((int unit));
    /* Close the protocol */
    void (*close) __P((int unit, char *reason));
    /* Print a packet in readable form */
    int  (*printpkt) __P((u_char *pkt, int len,
                          void (*printer) __P((void *, char *, ...)),
                          void *arg));
    /* Process a received data packet */
    void (*datainput) __P((int unit, u_char *pkt, int len));
    bool enabled_flag;                /* 0 iff protocol is disabled */
    char *name;                        /* Text name of protocol */
    char *data_name;                /* Text name of corresponding data protocol */
    option_t *options;                /* List of command-line options */
    /* Check requested options, assign defaults */
    void (*check_options) __P((void));
    /* Configure interface for demand-dial */
    int  (*demand_conf) __P((int unit));
    /* Say whether to bring up link for this pkt */
    int  (*active_pkt) __P((u_char *pkt, int len));
};


-----------------------------------------


upap.c的upap_rauthreq()函數做PAP認證,成功以後調用auth_peer_success()函數:
    if (retcode == UPAP_AUTHACK) {
        u->us_serverstate = UPAPSS_OPEN;
        notice("PAP peer authentication succeeded for %q", rhostname);
        auth_peer_success(u->us_unit, PPP_PAP, 0, ruser, ruserlen);
    } else {
上面函數中的u->us_unit:u是fsm結構,us_unit是第一個元素,在fsm.h中定義:
typedef struct fsm {
    int unit;                        /* Interface unit number */

auth.c文件下auth_peer_success()函數:
/*
* The peer has been successfully authenticated using `protocol'.
*/
void
auth_peer_success(unit, protocol, prot_flavor, name, namelen)
    int unit, protocol, prot_flavor;
    char *name;
    int namelen;
{

    switch (protocol) {

    ......

    case PPP_PAP:    /* 如果使用pap認證方式 */
        bit = PAP_PEER;
        break;

    ......

    /*
     * Save the authenticated name of the peer for later.
     */
    if (namelen > sizeof(peer_authname) - 1)
        namelen = sizeof(peer_authname) - 1;
    BCOPY(name, peer_authname, namelen);   /* 把用戶名保存在peer_authname中,以備將來使用,什麼地方使用這個變數還沒看到 */
    peer_authname = 0;
    script_setenv("PEERNAME", peer_authname, 0);

    /* Save the authentication method for later. */
    auth_done |= bit;

    /*
     * If there is no more authentication still to be done,
     * proceed to the network (or callback) phase.
     */
    if ((auth_pending &= ~bit) == 0)
        network_phase(unit);
}

[ 本帖最後由 sailer_sh 於 2006-2-10 21:48 編輯 ]
《解決方案》

main.c里有一個get_input()函數,它用於處理每個來自客戶端的數據包,當數據里包含用戶名和密碼里,它會執行相應的函數來處理用戶名和密碼。如果使用PAP認證方式,那麼函數會調用upap.c里的upap_input()函數,這個函數再調用同文件下的upap_rauthreq()函數,這個函數再調用auth.c文件里的check_passwd()函數處理用戶名和密碼。

main.c的get_input()函數:
u_char inpacket_buf; /* buffer for incoming packet */

    ......

    len = read_packet(inpacket_buf);

ppp_defs.h里定義:
#define PPP_MRU                1500        /* default MRU = max length of info field */

#define PPP_HDRLEN        4        /* octets for standard ppp header */
所以,ppp數據包的長度是1504個位元組,其實有4個位元組是ppp數據包頭部。

pppd服務端與客戶端之間的前5個數據包是LCP數據包,其中第三個LCP數據包調用了lcp_up()函數。第6個數據包用於客戶端用戶名和密碼的認證,如果是PAP認證方式,那麼lcp調用upap_input()處理這個數據包,upap_input()提取用戶名和密碼,做為參數傳遞給upap_rauthreq()進行認證,認證失敗就斷開連接,認證成功繼續傳輸數據包。
接下來的數據包有LCP和IPCP數據包,整個連接的第14個數據包是IPCP數據包,這個數據包包含VPN分配給客戶端的remoteIP(其實是由pptpd分配的),IPCP先調用了ipcp_up(),接下來ipcp_up()調用了auth_ip_addr(),並把remoteIP傳遞給它,auth_ip_addr()對remoteIP進行認證,認證方式是讀取/etc/ppp/pap-secrets,查看撥入用戶這一行的第四個欄位。認證成功以後繼續發送數據包,第15個數據包是LCP數據包。


未完待繼。。。

[ 本帖最後由 sailer_sh 於 2006-2-16 12:10 編輯 ]
《解決方案》

精品   還有繼續寫嗎? 我也在做linux ppp撥號 很多地方還不懂 想學習下
《解決方案》

好好學習下。現在也正在看這個東西呢。
《解決方案》

精彩!!!!!
《解決方案》

pppd命令怎麼用啊!讓它登陸adsl?急求。。
《解決方案》

我在一個車載項目中,使用GPRS和PPPD 出現一些問題,數據接收有問題,請求有償技術幫助,急急

我的聯繫:

mail:zhuweizz@126.com

qq:723974437

tel:13502865241



[火星人 via ] 分析pptpd程序中關於執行pptpd和pppd程序的部分源代碼已經有188次圍觀

http://www.coctec.com/docs/service/show-post-12046.html