1楼 发表于 2006-2-4 13:31 
最近使用pptpd部署*** 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[MAX_CONNECTIONS][16];
        /* localIP数组应该存放了所有本地ip,每个数组元素中存放一个ip,
           总的元素个数就是允许的最大连接数 */
extern char remoteIP[MAX_CONNECTIONS][16];

/* 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[16];
        char pppLocal[16];
};

        ......

/* 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[16];        /* 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[pptpctrl_argc++] = "0";
        ctrl_argv[pptpctrl_argc++] = "0";
#else
        /* specify local & remote addresses for this call */
        ctrl_argv[pptpctrl_argc++] = "1";
        ctrl_argv[pptpctrl_argc++] = localIP[clientNumber];  
        ctrl_argv[pptpctrl_argc++] = "1";
        ctrl_argv[pptpctrl_argc++] = remoteIP[clientNumber];

        ........

        /* 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[16];                /* local IP to pass to pppd */
        char pppRemote[16];                /* 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[2];
        char *pppaddrs[2] = { 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);

......

}[/code]

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

同一文件下的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[an++] = "local";

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

        if (pptpctrl_debug) {
                /* 以下两个if语句在系统日志文件里输出localip和remoteip,
                   在/var/log/messages里可以查到这些输出 */
                if (*pppaddrs[0])
                        syslog(LOG_DEBUG, "CTRL (PPPD Launcher): local address = %s", pppaddrs[0]);
                if (*pppaddrs[1])
                        syslog(LOG_DEBUG, "CTRL (PPPD Launcher): remote address = %s", pppaddrs[1]);
        }
       
        if (*pppaddrs[0] || *pppaddrs[1]) {
                char pppInterfaceIPs[33];
                sprintf(pppInterfaceIPs, "%s:%s", pppaddrs[0], pppaddrs[1]);
                pppd_argv[an++] = pppInterfaceIPs;
        }
#endif

        ......

        if (pptp_logwtmp) {                    /* logwtmp相关的代码 */
                 pppd_argv[an++] = "plugin";
                 pppd_argv[an++] = "/usr/lib/pptpd/pptpd-logwtmp.so";
                 pppd_argv[an++] = "pptpd-original-ip";
                 pppd_argv[an++] = inet_ntoa(inetaddrs[1]);
        }

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

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

关于上面函数中尾部的execvp(pppd_argv[0], pppd_argv)语句:
launch_pppd()函数里定义:

char *pppd_argv[14];

然后函数中对14个数组元素进行定义,其中第0个元素存放了pppd的执行程序:

pppd_argv[an++] = 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 编辑 ]

 
2楼 发表于 2006-2-10 20:57 
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[256], user[256];   /* 从这两个数组的定义可以猜到pppd可以接收的用户名和密码的最大长度都是256个字节*/
    char secret[MAXWORDLEN];
    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] != 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[256];
    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[unit];
    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[i]) != 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[namelen] = 0;
    script_setenv("PEERNAME", peer_authname, 0);

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

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

[ 本帖最后由 sailer_sh 于 2006-2-10 21:48 编辑 ]


 
 
3楼 发表于 2006-2-13 17:27 
main.c里有一个get_input()函数,它用于处理每个来自客户端的数据包,当数据里包含用户名和密码里,它会执行相应的函数来处理用户名和密码。如果使用PAP认证方式,那么函数会调用upap.c里的upap_input()函数,这个函数再调用同文件下的upap_rauthreq()函数,这个函数再调用auth.c文件里的check_passwd()函数处理用户名和密码。

main.c的get_input()函数:

u_char inpacket_buf[PPP_MRU+PPP_HDRLEN]; /* 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数据包,这个数据包包含×××分配给客户端的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 编辑 ]