pppoe plugin stuff - Michal Ostrowski's version, lightly hacked
authorPaul Mackerras <paulus@samba.org>
Thu, 26 Jul 2001 20:03:32 +0000 (20:03 +0000)
committerPaul Mackerras <paulus@samba.org>
Thu, 26 Jul 2001 20:03:32 +0000 (20:03 +0000)
12 files changed:
pppd/plugins/pppoe/Makefile.linux [new file with mode: 0644]
pppd/plugins/pppoe/libpppoe.c [new file with mode: 0644]
pppd/plugins/pppoe/pppd_utils.c [new file with mode: 0644]
pppd/plugins/pppoe/pppoe.c [new file with mode: 0644]
pppd/plugins/pppoe/pppoe.h [new file with mode: 0644]
pppd/plugins/pppoe/pppoe_client.c [new file with mode: 0644]
pppd/plugins/pppoe/pppoe_relay.c [new file with mode: 0644]
pppd/plugins/pppoe/pppoe_server.c [new file with mode: 0644]
pppd/plugins/pppoe/pppoed.c [new file with mode: 0644]
pppd/plugins/pppoe/pppoefwd.c [new file with mode: 0644]
pppd/plugins/pppoe/pppoehash.c [new file with mode: 0644]
pppd/plugins/pppoe/utils.c [new file with mode: 0644]

diff --git a/pppd/plugins/pppoe/Makefile.linux b/pppd/plugins/pppoe/Makefile.linux
new file mode 100644 (file)
index 0000000..7fc97eb
--- /dev/null
@@ -0,0 +1,42 @@
+CC     = gcc
+CFLAGS = -g  -I.. -I../.. -I../../../include -D_linux_=1 -fPIC
+LDFLAGS        = -shared
+INSTALL        = install -o root
+
+all: pppoe.so pppoed
+
+PLUGINDIR = $(LIBDIR)/plugins
+PLUGINSRCS= pppoe.c libpppoe.c utils.c pppoehash.c pppoe_client.c \
+               pppoe_relay.c pppoe_server.c pppd_utils.c
+
+# include dependencies if present
+ifeq (.depend,$(wildcard .depend))
+include .depend
+endif
+
+
+pppoefwd: pppoefwd.o libpppoe.a 
+       $(CC) -o $@ $^
+
+pppoed: pppoed.o pppd_utils.o libpppoe.a 
+       $(CC) -o $@ $^
+
+libpppoe.a: pppoehash.o pppoe_client.o pppoe_relay.o pppoe_server.o \
+       utils.o libpppoe.o 
+       ar -rc $@ $^
+
+pppoe.so: pppoe.o libpppoe.a
+       $(CC) -o $@ $(LDFLAGS) $^
+
+%.so: %.c
+       $(CC) -o $@ $(LDFLAGS) $(CFLAGS) $^
+
+clean: 
+       rm -f *.o *.so *.a pppoefwd pppoed
+
+install: all
+       $(INSTALL) -d -m 755 $(LIBDIR)
+       $(INSTALL) -s -c -m 4550 pppoe.so $(LIBDIR)
+
+depend:
+       $(CPP) -M $(CFLAGS) $(PLUGINSRCS) >.depend
diff --git a/pppd/plugins/pppoe/libpppoe.c b/pppd/plugins/pppoe/libpppoe.c
new file mode 100644 (file)
index 0000000..c0b5e5b
--- /dev/null
@@ -0,0 +1,631 @@
+/* PPPoE support library "libpppoe"
+ *
+ * Copyright 2000 Michal Ostrowski <mostrows@styx.uwaterloo.ca>,
+ *               Jamal Hadi Salim <hadi@cyberus.ca>
+ *
+ *  This program is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU General Public License
+ *  as published by the Free Software Foundation; either version
+ *  2 of the License, or (at your option) any later version.
+ */
+
+#include "pppoe.h"
+
+int disc_sock=-1;
+
+int verify_packet( struct session *ses, struct pppoe_packet *p);
+
+#define TAG_DATA(type,tag_ptr) ((type *) ((struct pppoe_tag*)tag_ptr)->tag_data)
+
+
+/***************************************************************************
+ *
+ * Return the location where the next tag can be pu
+ *
+ **************************************************************************/
+static  struct pppoe_tag *next_tag(struct pppoe_hdr *ph)
+{
+    return (struct pppoe_tag *)
+       (((char *) &ph->tag) + ntohs(ph->length));
+}
+
+/**************************************************************************
+ *
+ * Update header to reflect the addition of a new tag
+ *
+ **************************************************************************/
+static  void add_tag(struct pppoe_hdr *ph, struct pppoe_tag *pt)
+{
+    int len = (ntohs(ph->length) +
+              ntohs(pt->tag_len) +
+              sizeof(struct pppoe_tag));
+
+    if (pt != next_tag(ph))
+       printf("PPPoE add_tag caller is buggy\n");
+
+    ph->length = htons(len);
+}
+
+/*************************************************************************
+ *
+ * Look for a tag of a specific type
+ *
+ ************************************************************************/
+struct pppoe_tag *get_tag(struct pppoe_hdr *ph, u_int16_t idx)
+{
+    char *end = (char *) next_tag(ph);
+    char *ptn = NULL;
+    struct pppoe_tag *pt = &ph->tag[0];
+
+    /*
+     * Keep processing tags while a tag header will still fit.
+     *
+     * This check will ensure that the entire tag header pointed
+     * to by pt will fit inside the message, and thus it will be
+     * valid to check the tag_type and tag_len fields.
+     */
+    while ((char *)(pt + 1) <= end) {
+       /*
+        * If the tag data would go past the end of the packet, abort.
+        */
+       ptn = (((char *) (pt + 1)) + ntohs(pt->tag_len));
+       if (ptn > end)
+           return NULL;
+
+       if (pt->tag_type == idx)
+           return pt;
+
+       pt = (struct pppoe_tag *) ptn;
+    }
+
+    return NULL;
+}
+
+/* We want to use tag names to reference into arrays  containing the tag data.
+   This takes an RFC 2516 tag identifier and maps it into a local one.
+   The reverse mapping is accomplished via the tag_map array */
+#define UNMAP_TAG(x) case PTT_##x : return TAG_##x
+static inline int tag_index(int tag){
+    switch(tag){
+       UNMAP_TAG(SRV_NAME);
+       UNMAP_TAG(AC_NAME);
+       UNMAP_TAG(HOST_UNIQ);
+       UNMAP_TAG(AC_COOKIE);
+       UNMAP_TAG(VENDOR);
+       UNMAP_TAG(RELAY_SID);
+       UNMAP_TAG(SRV_ERR);
+       UNMAP_TAG(SYS_ERR);
+       UNMAP_TAG(GEN_ERR);
+       UNMAP_TAG(EOL);
+    };
+    return -1;
+}
+
+/*************************************************************************
+ *
+ * Makes a copy of a tag into a PPPoE packe
+ *
+ ************************************************************************/
+void copy_tag(struct pppoe_packet *dest, struct pppoe_tag *pt)
+{
+    struct pppoe_tag *end_tag = get_tag(dest->hdr, PTT_EOL);
+    int tagid;
+    int tag_len;
+    if( !pt ) {
+       return;
+    }
+    tagid = tag_index(pt->tag_type);
+
+    tag_len = sizeof(struct pppoe_tag) + ntohs(pt->tag_len);
+
+    if( end_tag ){
+       memcpy(((char*)end_tag)+tag_len ,
+              end_tag, sizeof(struct pppoe_tag));
+
+       dest->tags[tagid]=end_tag;
+       dest->tags[TAG_EOL] = (struct pppoe_tag*)((char*)dest->tags[TAG_EOL] + tag_len);
+       memcpy(end_tag, pt, tag_len);
+       dest->hdr->length = htons(ntohs(dest->hdr->length) + tag_len);
+
+    }else{
+       memcpy(next_tag(dest->hdr),pt, tag_len);
+       dest->tags[tagid]=next_tag(dest->hdr);
+       add_tag(dest->hdr,next_tag(dest->hdr));
+    }
+
+
+}
+
+
+/*************************************************************************
+ *
+ * Put tags from a packet into a nice array
+ *
+ ************************************************************************/
+static void extract_tags(struct pppoe_hdr *ph, struct pppoe_tag** buf){
+    int i=0;
+    for(;i<MAX_TAGS;++i){
+       buf[i] = get_tag(ph,tag_map[i]);
+    }
+}
+
+
+/*************************************************************************
+ *
+ * Verify that a packet has a tag containint a specific value
+ *
+ ************************************************************************/
+static int verify_tag(struct session* ses,
+                     struct pppoe_packet* p,
+                     unsigned short id,
+                     char* data,
+                     int data_len)
+{
+    int len;
+    struct pppoe_tag *pt = p->tags[id];
+
+    if( !pt ){
+       poe_info(ses,"Missing tag %d. Expected %s\n",
+                id,data);
+       return 0;
+    }
+    len = ntohs(pt->tag_len);
+    if(len != data_len){
+       poe_info(ses,"Length mismatch on tag %d: expect: %d got: %d\n",
+                id, data_len, len);
+       return 0;
+    }
+
+    if( 0!=memcmp(pt->tag_data,data,data_len)){
+       poe_info(ses,"Tag data mismatch on tag %d: expect: %s vs %s\n",
+                id, data,pt->tag_data);
+       return 0;
+    }
+    return 1;
+}
+
+
+/*************************************************************************
+ *
+ * Verify the existence of an ethernet device.
+ * Construct an AF_PACKET address struct to match.
+ *
+ ************************************************************************/
+int get_sockaddr_ll(const char *devnam,struct sockaddr_ll* sll){
+    struct ifreq ifr;
+    int retval;
+
+    if(disc_sock<0){
+
+       disc_sock = socket(PF_PACKET, SOCK_DGRAM, 0);
+       if( disc_sock < 0 ){
+           return -1;
+       }
+    }
+
+    strncpy(ifr.ifr_name, devnam, sizeof(ifr.ifr_name));
+
+    retval = ioctl( disc_sock , SIOCGIFINDEX, &ifr);
+
+    if( retval < 0 ){
+//     error("Bad device name: %s  (%m)",devnam);
+       return 0;
+    }
+
+    if(sll) sll->sll_ifindex = ifr.ifr_ifindex;
+
+    retval = ioctl (disc_sock, SIOCGIFHWADDR, &ifr);
+    if( retval < 0 ){
+//     error("Bad device name: %s  (%m)",devnam);
+       return 0;
+    }
+
+    if (ifr.ifr_hwaddr.sa_family != ARPHRD_ETHER) {
+       error("Interface %s is not Ethernet!", devnam);
+       return 0;
+    }
+    if(sll){
+       sll->sll_family = AF_PACKET;
+       sll->sll_protocol= ntohs(ETH_P_PPP_DISC);
+       sll->sll_hatype = ARPHRD_ETHER;
+       sll->sll_pkttype = PACKET_BROADCAST;
+       sll->sll_hatype = ETH_ALEN;
+       memcpy( sll->sll_addr , ifr.ifr_hwaddr.sa_data, ETH_ALEN);
+    }
+    return 1;
+}
+
+
+
+
+/*************************************************************************
+ *
+ * Construct and send a discovery message.
+ *
+ ************************************************************************/
+int send_disc(struct session *ses, struct pppoe_packet *p)
+{
+    char buf[MAX_PAYLOAD + sizeof(struct pppoe_hdr)];
+    int data_len = sizeof(struct pppoe_hdr);
+
+    struct pppoe_hdr *ph = NULL;
+    struct pppoe_tag *tag = NULL;
+    int i, error = 0;
+    int got_host_uniq = 0;
+    int got_srv_name = 0;
+    int got_ac_name = 0;
+
+    for (i = 0; i < MAX_TAGS; i++) {
+       if (!p->tags[i])
+           continue;
+
+       got_host_uniq |= (p->tags[i]->tag_type == PTT_HOST_UNIQ);
+
+       /* Relay identifiers qualify as HOST_UNIQ's:
+          we need HOST_UNIQ to uniquely identify the packet,
+          PTT_RELAY_SID is sufficient for us for outgoing packets */
+       got_host_uniq |= (p->tags[i]->tag_type == PTT_RELAY_SID);
+
+       got_srv_name |= (p->tags[i]->tag_type == PTT_SRV_NAME);
+       got_ac_name  |= (p->tags[i]->tag_type == PTT_AC_NAME);
+
+       data_len += (ntohs(p->tags[i]->tag_len) +
+                    sizeof(struct pppoe_tag));
+    }
+
+    ph = (struct pppoe_hdr *) buf;
+
+
+    memcpy(ph, p->hdr, sizeof(struct pppoe_hdr));
+    ph->length = __constant_htons(0);
+
+    /* if no HOST_UNIQ tags --- add one with process id */
+    if (!got_host_uniq){
+       data_len += (sizeof(struct pppoe_tag) +
+                    sizeof(struct session *));
+       tag = next_tag(ph);
+       tag->tag_type = PTT_HOST_UNIQ;
+       tag->tag_len = htons(sizeof(struct session *));
+       memcpy(tag->tag_data,
+              &ses,
+              sizeof(struct session *));
+
+       add_tag(ph, tag);
+    }
+
+    if( !got_srv_name ){
+       data_len += sizeof(struct pppoe_tag);
+       tag = next_tag(ph);
+       tag->tag_type = PTT_SRV_NAME;
+       tag->tag_len = 0;
+       add_tag(ph, tag);
+    }
+
+    if(!got_ac_name && ph->code==PADO_CODE){
+       data_len += sizeof(struct pppoe_tag);
+       tag = next_tag(ph);
+       tag->tag_type = PTT_AC_NAME;
+       tag->tag_len = 0;
+       add_tag(ph, tag);
+    }
+
+    for (i = 0; i < MAX_TAGS; i++) {
+       if (!p->tags[i])
+           continue;
+
+       tag = next_tag(ph);
+       memcpy(tag, p->tags[i],
+              sizeof(struct pppoe_tag) + ntohs(p->tags[i]->tag_len));
+
+       add_tag(ph, tag);
+    }
+
+    /* Now fixup the packet struct to make sure all of its pointers
+       are self-contained */
+    memcpy( p->hdr , ph, data_len );
+    extract_tags( p->hdr, p->tags);
+
+    error = sendto(disc_sock, buf, data_len, 0,
+                  (struct sockaddr*) &p->addr,
+                  sizeof(struct sockaddr_ll));
+
+    if(error < 0)
+       poe_error(ses,"sendto returned: %m\n");
+
+    return error;
+}
+
+/*************************************************************************
+ *
+ * Verify that a packet is legal
+ *
+ *************************************************************************/
+int verify_packet( struct session *ses, struct pppoe_packet *p){
+    struct session * hu_val;
+
+    /* This code here should do all of the error checking and
+       validation on the incoming packet */
+
+
+    /* If we receive any error tags, abort */
+#define CHECK_TAG(name, val)                                   \
+    if((NULL==p->tags[name])== val){                           \
+       poe_error(ses,"Tag error: " #name );                    \
+       return -1;                                              \
+    }
+
+
+
+    CHECK_TAG(TAG_SRV_ERR,0);
+    CHECK_TAG(TAG_SYS_ERR,0);
+    CHECK_TAG(TAG_GEN_ERR,0);
+
+    /* A HOST_UNIQ must be present */
+    CHECK_TAG(TAG_HOST_UNIQ,1);
+
+    hu_val = *TAG_DATA(struct session* ,p->tags[TAG_HOST_UNIQ]);
+
+    if( hu_val != ses ){
+       poe_info(ses,"HOST_UNIQ mismatch: %08x %i\n",(int)hu_val,getpid());
+       return -1;
+    }
+
+    if(ses->filt->htag &&
+       !verify_tag(ses,p,TAG_HOST_UNIQ,ses->filt->htag->tag_data,(int)ntohs(ses->filt->htag->tag_len)))
+       return -1;
+    else
+       poe_info(ses,"HOST_UNIQ successful match\n");
+
+
+    if(ses->filt->ntag &&
+       !verify_tag(ses,p,TAG_AC_NAME,ses->filt->ntag->tag_data,(int)ntohs(ses->filt->ntag->tag_len))){
+       poe_info(ses,"AC_NAME failure");
+       return -1;
+    }
+
+    if(ses->filt->stag &&
+       !verify_tag(ses,p,TAG_SRV_NAME,ses->filt->stag->tag_data,(int)ntohs(ses->filt->stag->tag_len))){
+       poe_info(ses,"SRV_NAME failure");
+       return -1;
+    }
+
+}
+
+
+/*************************************************************************
+ *
+ * Receive and verify an incoming packet.
+ *
+ *************************************************************************/
+static int recv_disc( struct session *ses,
+                     struct pppoe_packet *p){
+    int error = 0;
+    unsigned int from_len = sizeof(struct sockaddr_ll);
+    struct session* hu_val;
+    struct pppoe_tag *pt;
+
+    p->hdr = (struct pppoe_hdr*)p->buf;
+
+    error = recvfrom( disc_sock, p->buf, 1500, 0,
+                     (struct sockaddr*)&p->addr, &from_len);
+
+    if(error < 0) return error;
+
+    extract_tags(p->hdr,p->tags);
+
+    return 1;
+}
+
+
+/*************************************************************************
+ *
+ * Send a PADT
+ *
+ *************************************************************************/
+int session_disconnect(struct session *ses){
+    struct pppoe_packet padt;
+
+    memset(&padt,0,sizeof(struct pppoe_packet));
+    memcpy(&padt.addr, &ses->remote, sizeof(struct sockaddr_ll));
+
+    padt.hdr = (struct pppoe_hdr*) ses->curr_pkt.buf;
+    padt.hdr->ver  = 1;
+    padt.hdr->type = 1;
+    padt.hdr->code = PADT_CODE;
+    padt.hdr->sid  = ses->sp.sa_addr.pppoe.sid;
+
+    send_disc(ses,&padt);
+    ses->sp.sa_addr.pppoe.sid = 0 ;
+    ses->state = PADO_CODE;
+    return 0;
+
+}
+
+
+/*************************************************************************
+ *
+ * Make a connection -- behaviour depends on callbacks specified in "ses"
+ *
+ *************************************************************************/
+int session_connect(struct session *ses)
+{
+
+    int pkt_size=0;
+    int ret_pkt_size=0;
+    struct pppoe_tag *tags = NULL;
+    struct pppoe_packet *p_out=NULL;
+    struct pppoe_packet rcv_packet;
+    int ret;
+
+
+    if(ses->init_disc){
+       ret = (*ses->init_disc)(ses, NULL, &p_out);
+       if( ret != 0 ) return ret;
+    }
+
+    /* main discovery loop */
+
+
+    while(ses->retransmits < ses->retries || ses->retries==-1 ){
+
+       fd_set in;
+       struct timeval tv;
+       FD_ZERO(&in);
+
+       FD_SET(disc_sock,&in);
+
+       if(ses->retransmits>=0){
+           ++ses->retransmits;
+           tv.tv_sec = 1 << ses->retransmits;
+           tv.tv_usec = 0;
+           ret = select(disc_sock+1, &in, NULL, NULL, &tv);
+       }else{
+           ret = select(disc_sock+1, &in, NULL, NULL, NULL);
+       }
+
+       if( ret == 0 ){
+           if( DEB_DISC ){
+               poe_dbglog(ses, "Re-sending ...");
+           }
+
+           if( ses->timeout ){
+               ret = (*ses->timeout)(ses, NULL, &p_out);
+               if( ret != 0 )
+                   return ret;
+
+           }else if(p_out){
+               send_disc(ses,p_out);
+           }
+           continue;
+       }
+
+
+       ret = recv_disc(ses, &rcv_packet);
+
+       /* Should differentiate between system errors and
+          bad packets and the like... */
+       if( ret < 0 && errno != EINTR){
+
+           return -1;
+       }
+
+
+
+
+       switch (rcv_packet.hdr->code) {
+
+       case PADI_CODE:
+       {
+           if(ses->rcv_padi){
+               ret = (*ses->rcv_padi)(ses,&rcv_packet,&p_out);
+
+               if( ret != 0){
+                   return ret;
+               }
+           }
+           break;
+       }
+
+       case PADO_CODE:         /* wait for PADO */
+       {
+           if(ses->rcv_pado){
+               ret = (*ses->rcv_pado)(ses,&rcv_packet,&p_out);
+
+               if( ret != 0){
+                   return ret;
+               }
+           }
+           break;
+       }
+
+       case PADR_CODE:
+       {
+           if(ses->rcv_padr){
+               ret = (*ses->rcv_padr)(ses,&rcv_packet,&p_out);
+
+               if( ret != 0){
+                   return ret;
+               }
+           }
+           break;
+       }
+
+       case PADS_CODE:
+       {
+           if(ses->rcv_pads){
+               ret = (*ses->rcv_pads)(ses,&rcv_packet,&p_out);
+
+               if( ret != 0){
+                   return ret;
+               }
+           }
+           break;
+       }
+
+       case PADT_CODE:
+       {
+           if( rcv_packet.hdr->sid != ses->sp.sa_addr.pppoe.sid ){
+               --ses->retransmits;
+               continue;
+           }
+           if(ses->rcv_padt){
+               ret = (*ses->rcv_padt)(ses,&rcv_packet,&p_out);
+
+               if( ret != 0){
+                   return ret;
+               }
+           }else{
+               poe_error (ses,"connection terminated");
+               return (-1);
+           }
+           break;
+       }
+       default:
+           poe_error(ses,"invalid packet %P",&rcv_packet);
+           return (-1);
+       }
+    }
+    return (0);
+}
+
+
+/*************************************************************************
+ *
+ * Register an ethernet address as a client of relaying services.
+ *
+ *************************************************************************/
+int add_client(char *addr)
+{
+    struct pppoe_con* pc = (struct pppoe_con*)malloc(sizeof(struct pppoe_con));
+    int ret;
+    if(!pc)
+       return -ENOMEM;
+
+    memset(pc, 0 , sizeof(struct pppoe_con));
+
+    memcpy(pc->client,addr, ETH_ALEN);
+    memcpy(pc->key, addr, ETH_ALEN);
+
+    pc->key_len = ETH_ALEN;
+
+    if( (ret=store_con(pc)) < 0 ){
+       free(pc);
+    }
+    return ret;
+
+}
+
+struct pppoe_tag *make_filter_tag(short type, short length, char* data){
+    struct pppoe_tag *pt =
+       (struct pppoe_tag* )malloc( sizeof(struct pppoe_tag) + length );
+
+    if(pt == NULL) return NULL;
+
+    pt->tag_len=htons(length);
+    pt->tag_type=type;
+
+    if(length>0 && data){
+       memcpy( pt+1, data, length);
+    }
+    return pt;
+}
diff --git a/pppd/plugins/pppoe/pppd_utils.c b/pppd/plugins/pppoe/pppd_utils.c
new file mode 100644 (file)
index 0000000..86a2ca7
--- /dev/null
@@ -0,0 +1,162 @@
+/* PPPoE support library "libpppoe"
+ *
+ * Copyright 2000 Michal Ostrowski <mostrows@styx.uwaterloo.ca>,
+ *               Jamal Hadi Salim <hadi@cyberus.ca>
+ *
+ *  This program is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU General Public License
+ *  as published by the Free Software Foundation; either version
+ *  2 of the License, or (at your option) any later version.
+ */
+#include "pppoe.h"
+
+/*
+ *
+ */
+int build_ppp_opts(char *args[],struct session *ses)
+{
+    char buf[256];
+    int retval=0,i=0;
+    
+    memset(buf,0,256);
+    
+/* pppds path */
+    if ( NULL != ses->filt->pppd){
+       args[0]=(char *)malloc(strlen(ses->filt->pppd));
+        strcpy (args[0],ses->filt->pppd);
+    } else {
+       args[0]=(char *)malloc(strlen(_PATH_PPPD));
+        strcpy (args[0],_PATH_PPPD);
+    }
+    
+/*  long device name */
+    snprintf(buf, 256,"%02x:%02x:%02x:%02x:%02x:%02x/%04x/%s",
+            ses->remote.sll_addr[0],
+            ses->remote.sll_addr[1],
+            ses->remote.sll_addr[2],
+            ses->remote.sll_addr[3],
+            ses->remote.sll_addr[4],
+            ses->remote.sll_addr[5],
+            ses->sp.sa_addr.pppoe.sid,
+            ses->name);
+    args[1]=(char *)malloc(strlen(buf));
+    strcpy(args[1],buf);
+    
+    i=2;
+    
+/* override options file */
+    if (NULL != ses->filt->fname ) {
+       
+       if (!ses->filt->peermode) {
+           args[i]=(char *)malloc(strlen("file"));
+           strcpy (args[i],"file");
+           i++;
+           args[i]=(char *)malloc(strlen(ses->filt->fname)+1);
+           strcpy (args[i],ses->filt->fname);
+           i++;
+       } else{ /* peermode */
+           args[i]=(char *)malloc(strlen("call"));
+           strcpy (args[i],"call");
+           i++;
+           args[i]=(char *)malloc(strlen(ses->filt->fname)+1);
+           strcpy (args[i],ses->filt->fname);
+           i++;
+       }
+    }
+    
+/* user requested for a specific name */
+    if (NULL != ses->filt->ntag) {
+       if ( NULL != ses->filt->ntag->tag_data) {
+           args[i]=(char *)malloc(strlen("pppoe_ac_name"));
+           strcpy(args[i],"pppoe_ac_name");
+           i++;
+           args[i]=(char *)malloc(ntohs(ses->filt->ntag->tag_len));
+           strcpy(args[i],ses->filt->ntag->tag_data);
+           i++;
+       }
+    }
+/* user requested for a specific service name */
+    if (NULL != ses->filt->stag) {
+       if ( NULL != ses->filt->stag->tag_data) {
+           args[i]=(char *)malloc(strlen("pppoe_srv_name"));
+           strcpy(args[i],"pppoe_srv_name");
+           i++;
+           args[i]=(char *)malloc(ntohs(ses->filt->stag->tag_len));
+           strcpy(args[i],ses->filt->stag->tag_data);
+           i++;
+       }
+    }
+    
+/*
+ */
+    if (ses->opt_daemonize) {
+       args[i]=(char *)malloc(strlen("nodetach"));
+       strcpy(args[i],"nodetach");
+       i++;
+    }
+    
+    args[i]=NULL;
+    {
+       int j;
+       poe_info(ses,"calling pppd with %d args\n",i);
+       j=i;
+       for (i=0; i<j,NULL !=args[i]; i++) {
+           poe_info(ses," <%d: %s > \n",i,args[i]);
+       }
+    }
+    return retval;
+}
+
+
+/*
+ *
+ */
+int ppp_connect (struct session *ses)
+{
+    int ret,pid;
+    char *args[32];
+    
+    
+    poe_info(ses,"calling ses_connect\n");
+    do{
+       ret = session_connect(ses);
+    }while(ret == 0);
+
+    if (ret > 0 )
+       if (ses->np == 1 && ret == 1)
+           return ses->np; /* -G */
+    if (ses->np == 2)
+       return ses->np; /* -H */
+
+    if( ret <= 0){
+       return ret;
+    }
+    
+    poe_info(ses,"DONE calling ses_connect np is %d \n",ses->np);
+    
+    
+    pid = fork ();
+    if (pid < 0) {
+       poe_error (ses,"unable to fork() for pppd: %m");
+       poe_die (-1);
+    }
+    
+    
+    if(!pid) {
+       poe_info(ses,"calling build_ppp_opts\n");
+       if (0> build_ppp_opts(args,ses)) {
+           poe_error(ses,"ppp_connect: failed to build ppp_opts\n");
+           return -1;
+       }
+       execvp(args[0],args);
+       poe_info (ses," child got killed");
+    } else if( ses->type == SESSION_CLIENT) {
+       if (!ses->opt_daemonize)
+           return 1;
+       pause();
+       poe_info (ses," OK we got killed");
+       return -1;
+    }
+    return 1;
+}
+
diff --git a/pppd/plugins/pppoe/pppoe.c b/pppd/plugins/pppoe/pppoe.c
new file mode 100644 (file)
index 0000000..773e476
--- /dev/null
@@ -0,0 +1,369 @@
+/* pppoe.c - pppd plugin to implement PPPoE protocol.
+ *
+ * Copyright 2000 Michal Ostrowski <mostrows@styx.uwaterloo.ca>,
+ *               Jamal Hadi Salim <hadi@cyberus.ca>
+ * Borrows heavily from the PPPoATM plugin by Mitchell Blank Jr.,
+ * which is based in part on work from Jens Axboe and Paul Mackerras.
+ *
+ *  This program is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU General Public License
+ *  as published by the Free Software Foundation; either version
+ *  2 of the License, or (at your option) any later version.
+ */
+
+#include <string.h>
+#include <sys/ioctl.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <unistd.h>
+#include <errno.h>
+#include <sys/stat.h>
+#include "pppoe.h"
+#if _linux_
+extern int new_style_driver;    /* From sys-linux.c */
+#include <linux/ppp_defs.h>
+#include <linux/if_pppox.h>
+#include <linux/if_ppp.h>
+#else
+#error this module meant for use with linux only at this time
+#endif
+
+
+#include "pppd.h"
+#include "fsm.h"
+#include "lcp.h"
+#include "ipcp.h"
+#include "ccp.h"
+#include "pathnames.h"
+#define _PATH_ETHOPT         _ROOT_PATH "/etc/ppp/options."
+
+#define PPPOE_MTU      1492
+extern int kill_link;
+static char *bad_options[] = {
+    "noaccomp",
+    "-ac",
+    "default-asyncmap",
+    "-am",
+    "asyncmap",
+    "-as",
+    "escape",
+    "multilink",
+    "receive-all",
+    "crtscts",
+    "-crtscts",
+    "nocrtscts",
+    "cdtrcts",
+    "nocdtrcts",
+    "xonxoff",
+    "modem",
+    "local",
+    "sync",
+    "deflate",
+    "nodeflate",
+    "vj",
+    "novj",
+    "nobsdcomp",
+    "bsdcomp",
+    "-bsdcomp",
+    NULL
+};
+
+bool   pppoe_server=0;
+char   *pppoe_srv_name=NULL;
+char   *pppoe_ac_name=NULL;
+char    *hostuniq = NULL;
+int     retries = 0;
+
+int setdevname_pppoe(const char *cp);
+
+static option_t pppoe_options[] = {
+       { "device name", o_wild, (void *) &setdevname_pppoe,
+         "Serial port device name",
+         OPT_DEVNAM | OPT_PRIVFIX | OPT_NOARG  | OPT_A2STRVAL | OPT_STATIC,
+         devnam},
+       { "pppoe_srv_name", o_string, &pppoe_srv_name,
+         "PPPoE service name"},
+       { "pppoe_ac_name", o_string, &pppoe_ac_name,
+         "PPPoE access concentrator name"},
+       { "pppoe_hostuniq", o_string, &hostuniq,
+         "PPPoE client uniq hostid "},
+       { "pppoe_retransmit", o_int, &retries,
+         "PPPoE client number of retransmit tries"},
+       { "pppoe_server", o_bool, &pppoe_server,
+         "PPPoE listen for incoming requests",1},
+       { NULL }
+};
+
+
+
+struct session *ses = NULL;
+static int connect_pppoe_ses(void)
+{
+    int i,err=-1;
+    if( pppoe_server == 1 ){
+       srv_init_ses(ses,devnam);
+    }else{
+       client_init_ses(ses,devnam);
+    }
+#if 0
+    ses->np=1;  /* jamal debug the discovery portion */
+#endif
+    strlcpy(ppp_devnam, devnam, sizeof(ppp_devnam));
+
+    err= session_connect ( ses );
+
+    if(err < 0){
+       poe_fatal(ses,"Failed to negotiate PPPoE connection: %d %m",errno,errno);
+    }
+
+
+    poe_info(ses,"Connecting PPPoE socket: %E %04x %s %p",
+            ses->sp.sa_addr.pppoe.remote,
+            ses->sp.sa_addr.pppoe.sid,
+            ses->sp.sa_addr.pppoe.dev,ses);
+
+    err = connect(ses->fd, (struct sockaddr*)&ses->sp,
+                 sizeof(struct sockaddr_pppox));
+
+
+    if( err < 0 ){
+       poe_fatal(ses,"Failed to connect PPPoE socket: %d %m",errno,errno);
+       return err;
+    }
+#if 0
+    if (ses->np)
+       fatal("discovery complete\n");
+#endif
+    /* Once the logging is fixed, print a message here indicating
+       connection parameters */
+
+    return ses->fd;
+}
+
+static void disconnect_pppoe_ses(void)
+{
+    int ret;
+    warn("Doing disconnect");
+    session_disconnect(ses);
+    ses->sp.sa_addr.pppoe.sid = 0;
+    ret = connect(ses->fd, (struct sockaddr*)&ses->sp,
+           sizeof(struct sockaddr_pppox));
+
+}
+
+
+static int setspeed_pppoe(const char *cp)
+{
+    return 0;
+}
+
+static void init_device_pppoe(void)
+{
+    struct filter *filt;
+    unsigned int size=0;
+    ses=(void *)malloc(sizeof(struct session));
+    if(!ses){
+       fatal("No memory for new PPPoE session");
+    }
+    memset(ses,0,sizeof(struct session));
+
+    if ((ses->filt=malloc(sizeof(struct filter))) == NULL) {
+       poe_error (ses,"failed to malloc for Filter ");
+       poe_die (-1);
+    }
+
+    filt=ses->filt;  /* makes the code more readable */
+    memset(filt,0,sizeof(struct filter));
+
+    if (pppoe_ac_name !=NULL) {
+       if (strlen (pppoe_ac_name) > 255) {
+           poe_error (ses," AC name too long (maximum allowed 256 chars)");
+           poe_die(-1);
+       }
+       ses->filt->ntag = make_filter_tag(PTT_AC_NAME,
+                                         strlen(pppoe_ac_name),
+                                         pppoe_ac_name);
+
+       if ( ses->filt->ntag== NULL) {
+           poe_error (ses,"failed to malloc for AC name");
+           poe_die(-1);
+       }
+
+    }
+
+
+    if (pppoe_srv_name !=NULL) {
+       if (strlen (pppoe_srv_name) > 255) {
+           poe_error (ses," Service name too long
+                       (maximum allowed 256 chars)");
+           poe_die(-1);
+       }
+       ses->filt->stag = make_filter_tag(PTT_SRV_NAME,
+                                         strlen(pppoe_srv_name),
+                                         pppoe_srv_name);
+       if ( ses->filt->stag == NULL) {
+           poe_error (ses,"failed to malloc for service name");
+           poe_die(-1);
+       }
+    }
+
+    if (hostuniq) {
+       ses->filt->htag = make_filter_tag(PTT_HOST_UNIQ,
+                                         strlen(hostuniq),
+                                         hostuniq);
+       if ( ses->filt->htag == NULL) {
+           poe_error (ses,"failed to malloc for Uniq Host Id ");
+           poe_die(-1);
+       }
+    }
+
+    if (retries) {
+       ses->retries=retries;
+    }
+
+    memcpy( ses->name, devnam, IFNAMSIZ);
+    ses->opt_debug=1;
+}
+
+static void pppoe_extra_options()
+{
+    int ret;
+    char buf[256];
+    snprintf(buf, 256, _PATH_ETHOPT "%s",devnam);
+    if(!options_from_file(buf, 0, 0, 1))
+       exit(EXIT_OPTION_ERROR);
+
+}
+
+
+
+static void send_config_pppoe(int mtu,
+                             u_int32_t asyncmap,
+                             int pcomp,
+                             int accomp)
+{
+    int sock;
+    struct ifreq ifr;
+
+    if (mtu > PPPOE_MTU) {
+       warn("Not increasing MTU to %d, max is %d", mtu, PPPOE_MTU);
+       return;
+    }
+}
+
+
+static void recv_config_pppoe(int mru,
+                             u_int32_t asyncmap,
+                             int pcomp,
+                             int accomp)
+{
+    if (mru > PPPOE_MTU)
+       error("Not increasing MRU to %d, max is %d", mru, PPPOE_MTU);
+}
+
+static void set_xaccm_pppoe(int unit, ext_accm accm)
+{
+    /* NOTHING */
+}
+
+
+
+struct channel pppoe_channel;
+/* Check is cp is a valid ethernet device
+ * return either 1 if "cp" is a reasonable thing to name a device
+ * or die.
+ * Note that we don't actually open the device at this point
+ * We do need to fill in:
+ *   devnam: a string representation of the device
+ */
+
+int (*old_setdevname_hook)(const char* cp) = NULL;
+int setdevname_pppoe(const char *cp)
+{
+    int ret;
+    char dev[IFNAMSIZ+1];
+    int addr[ETH_ALEN];
+    int sid;
+
+    char **a;
+    ret =sscanf(cp, FMTSTRING(IFNAMSIZ),addr, addr+1, addr+2,
+               addr+3, addr+4, addr+5,&sid,dev);
+    if( ret != 8 ){
+
+       ret = get_sockaddr_ll(cp,NULL);
+        if (ret < 0)
+           fatal("PPPoE: Cannot create PF_PACKET socket for PPPoE discovery\n");
+       if (ret == 1)
+           strncpy(devnam, cp, sizeof(devnam));
+    }else{
+       /* long form parsed */
+       ret = get_sockaddr_ll(dev,NULL);
+        if (ret < 0)
+           fatal("PPPoE: Cannot create PF_PACKET socket for PPPoE discovery\n");
+
+       strncpy(devnam, cp, sizeof(devnam));
+       ret = 1;
+    }
+
+
+    if( ret == 1 && the_channel != &pppoe_channel ){
+
+       the_channel = &pppoe_channel;
+
+       lcp_allowoptions[0].neg_accompression = 0;
+       lcp_wantoptions[0].neg_accompression = 0;
+
+       lcp_allowoptions[0].neg_asyncmap = 0;
+       lcp_wantoptions[0].neg_asyncmap = 0;
+
+       lcp_allowoptions[0].neg_pcompression = 0;
+       lcp_wantoptions[0].neg_pcompression = 0;
+
+       ccp_allowoptions[0].deflate = 0 ;
+       ccp_wantoptions[0].deflate = 0 ;
+
+       ipcp_allowoptions[0].neg_vj=0;
+       ipcp_wantoptions[0].neg_vj=0;
+
+       ccp_allowoptions[0].bsd_compress = 0;
+       ccp_wantoptions[0].bsd_compress = 0;
+
+       init_device_pppoe();
+    }
+    return ret;
+}
+
+
+
+void plugin_init(void)
+{
+/*
+  fatal("PPPoE plugin loading...");
+*/
+
+#if _linux_
+    if (!ppp_available() && !new_style_driver)
+       fatal("Kernel doesn't support ppp_generic needed for PPPoE");
+#else
+    fatal("No PPPoE support on this OS");
+#endif
+    add_options(pppoe_options);
+
+
+    info("PPPoE Plugin Initialized");
+}
+
+struct channel pppoe_channel = {
+    options: pppoe_options,
+    process_extra_options: &pppoe_extra_options,
+    check_options: NULL,
+    connect: &connect_pppoe_ses,
+    disconnect: &disconnect_pppoe_ses,
+    establish_ppp: &generic_establish_ppp,
+    disestablish_ppp: &generic_disestablish_ppp,
+    send_config: &send_config_pppoe,
+    recv_config: &recv_config_pppoe,
+    close: NULL,
+    cleanup: NULL
+};
+
diff --git a/pppd/plugins/pppoe/pppoe.h b/pppd/plugins/pppoe/pppoe.h
new file mode 100644 (file)
index 0000000..4490cc9
--- /dev/null
@@ -0,0 +1,273 @@
+/* PPPoE support library "libpppoe"
+ *
+ * Copyright 2000 Michal Ostrowski <mostrows@styx.uwaterloo.ca>,
+ *               Jamal Hadi Salim <hadi@cyberus.ca>
+ *
+ *  This program is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU General Public License
+ *  as published by the Free Software Foundation; either version
+ *  2 of the License, or (at your option) any later version.
+ */
+
+#ifndef PPPOE_H
+#define PPPOE_H        1
+#include <stdio.h>             /* stdio               */
+#include <stdlib.h>            /* strtoul(), realloc() */
+#include <unistd.h>            /* STDIN_FILENO,exec    */
+#include <string.h>            /* memcpy()             */
+#include <errno.h>             /* errno                */
+#include <signal.h>
+#include <getopt.h>
+#include <stdarg.h>
+#include <syslog.h>
+#include <paths.h>
+
+#include <sys/types.h>         /* socket types         */
+#include <asm/types.h>
+#include <sys/time.h>
+#include <sys/wait.h>
+#include <sys/fcntl.h>
+#include <sys/ioctl.h>         /* ioctl()              */
+#include <sys/select.h>
+#include <sys/socket.h>                /* socket()             */
+#include <net/if.h>            /* ifreq struct         */
+#include <net/if_arp.h>
+#include <netinet/in.h>
+
+#if __GLIBC__ >= 2 && __GLIBC_MINOR >= 1
+#include <netpacket/packet.h>
+//#include <net/ethernet.h>
+#else
+#include <asm/types.h>
+#include <linux/if_packet.h>
+#include <linux/if_ether.h>
+#endif
+
+
+#include <asm/byteorder.h>
+
+/*
+  jamal: we really have to change this
+  to make it compatible to the 2.2 and
+  2.3 kernel
+*/
+
+#include <linux/if_pppox.h>
+
+
+#define CONNECTED 1
+#define DISCONNECTED 0
+
+#ifndef _PATH_PPPD
+#define _PATH_PPPD "/usr/sbin/pppd"
+#endif
+
+#ifndef LOG_PPPOE
+#define LOG_PPPOE LOG_DAEMON
+#endif
+
+
+#define VERSION_MAJOR 0
+#define VERSION_MINOR 4
+#define VERSION_DATE 991120
+
+/* Bigger than the biggest ethernet packet we'll ever see, in bytes */
+#define MAX_PACKET      2000
+
+/* references: RFC 2516 */
+/* ETHER_TYPE fields for PPPoE */
+
+#define ETH_P_PPPOE_DISC 0x8863        /* discovery stage */
+#define ETH_P_PPPOE_SESS 0x8864
+
+/* ethernet broadcast address */
+#define MAC_BCAST_ADDR "\xff\xff\xff\xff\xff\xff"
+
+/* Format for parsing long device-name */
+#define _STR(x) #x
+#define FMTSTRING(size) "%x:%x:%x:%x:%x:%x/%x/%" _STR(size) "s"
+
+/* maximum payload length */
+#define MAX_PAYLOAD 1484
+
+
+
+/* PPPoE tag types */
+#define MAX_TAGS               11
+
+
+/* PPPoE packet; includes Ethernet headers and such */
+struct pppoe_packet{
+       struct sockaddr_ll addr;
+       struct pppoe_tag *tags[MAX_TAGS];
+       struct pppoe_hdr *hdr;
+       char buf[MAX_PAYLOAD];          /* buffer in which tags are held */
+};
+/* Defines meaning of each "tags" element */
+
+#define TAG_SRV_NAME   0
+#define TAG_AC_NAME    1
+#define TAG_HOST_UNIQ  2
+#define TAG_AC_COOKIE  3
+#define TAG_VENDOR     4
+#define TAG_RELAY_SID  5
+#define TAG_SRV_ERR     6
+#define TAG_SYS_ERR    7
+#define TAG_GEN_ERR    8
+#define TAG_EOL                9
+
+static int tag_map[] = { PTT_SRV_NAME,
+                        PTT_AC_NAME,
+                        PTT_HOST_UNIQ,
+                        PTT_AC_COOKIE,
+                        PTT_VENDOR,
+                        PTT_RELAY_SID,
+                        PTT_SRV_ERR,
+                        PTT_SYS_ERR,
+                        PTT_GEN_ERR,
+                        PTT_EOL
+};
+
+
+/* Debug flags */
+int DEB_DISC,DEB_DISC2;
+/*
+  #define DEB_DISC             (opt_debug & 0x0002)
+  #define DEB_DISC2            (opt_debug & 0x0004)
+*/
+#define MAX_FNAME              256
+
+
+struct session;
+
+/* return <0 --> fatal error; abor
+   return =0 --> ok, proceed
+   return >0 --> ok, qui
+*/
+typedef int (*packet_cb_t)(struct session* ses,
+                          struct pppoe_packet *p_in,
+                          struct pppoe_packet **p_out);
+
+/* various override filter tags */
+struct filter {
+       struct pppoe_tag *stag;  /* service name tag override */
+       struct pppoe_tag *ntag;  /*AC name override */
+       struct pppoe_tag *htag;  /* hostuniq override */
+       int num_restart;
+       int peermode;
+       char *fname;
+       char *pppd;
+} __attribute__ ((packed));
+
+
+struct pppoe_tag *make_filter_tag(short type, short length, char* data);
+
+/* Session type definitions */
+#define SESSION_CLIENT 0
+#define SESSION_SERVER 1
+#define SESSION_RELAY  2
+
+struct session {
+
+       /* Administrative */
+       int type;
+       int opt_debug;
+       int detached;
+       int np;
+       int log_to_fd;
+       int ifindex;                    /* index of device */
+       char name[IFNAMSIZ];            /*dev name */
+       struct pppoe_packet curr_pkt;
+
+       packet_cb_t init_disc;
+       packet_cb_t rcv_pado;
+       packet_cb_t rcv_padi;
+       packet_cb_t rcv_pads;
+       packet_cb_t rcv_padr;
+       packet_cb_t rcv_padt;
+       packet_cb_t timeout;
+
+
+       /* Generic */
+       struct filter *filt;
+       struct sockaddr_ll local;
+       struct sockaddr_ll remote;
+       struct sockaddr_pppox sp;
+       int fd;                         /* fd of PPPoE socket */
+
+
+       /* For client */
+       int retransmits;                /* Number of retransmission performed
+                                          if < 0 , retransmissions disabled */
+       int retries;
+       int state;
+       int opt_daemonize;
+
+       /* For server */
+       int fork;
+
+       /* For forwarding */
+       int fwd_sock;
+       char fwd_name[IFNAMSIZ];        /* Name of device to forward to */
+} __attribute__ ((packed));
+
+/*
+  retransmit retries for the PADR and PADI packets
+  during discovery
+*/
+int PADR_ret;
+int PADI_ret;
+
+int log_to_fd;
+int ctrl_fd;
+int opt_debug;
+int opt_daemonize;
+
+
+/* Structure for keeping track of connection relays */
+struct pppoe_con{
+       struct pppoe_con *next;
+       int id;
+       int connected;
+       int  cl_sock;
+       int  sv_sock;
+       int ref_count;
+       char client[ETH_ALEN];
+       char server[ETH_ALEN];
+       char key_len;
+       char key[32];
+};
+
+/* Functions exported from utils.c. */
+
+/* Functions exported from pppoehash.c */
+struct pppoe_con *get_con(int len, char *key);
+int store_con(struct pppoe_con *pc);
+struct pppoe_con *delete_con(unsigned long len, char *key);
+
+/* exported by lib.c */
+
+extern int init_lib();
+
+extern int get_sockaddr_ll(const char *devnam,struct sockaddr_ll* sll);
+
+extern int client_init_ses (struct session *ses, char* devnam);
+extern int relay_init_ses(struct session *ses, char* from, char* to);
+extern int srv_init_ses(struct session *ses, char* from);
+extern int session_connect(struct session *ses);
+extern int session_disconnect(struct session*ses);
+
+extern int verify_packet( struct session *ses, struct pppoe_packet *p);
+
+extern void copy_tag(struct pppoe_packet *dest, struct pppoe_tag *pt);
+extern struct pppoe_tag *get_tag(struct pppoe_hdr *ph, u_int16_t idx);
+extern int send_disc(struct session *ses, struct pppoe_packet *p);
+
+
+extern int add_client(char *addr);
+
+/* Make connections (including spawning pppd) as server/client */
+extern ppp_connect(struct session *ses);
+
+
+#endif
diff --git a/pppd/plugins/pppoe/pppoe_client.c b/pppd/plugins/pppoe/pppoe_client.c
new file mode 100644 (file)
index 0000000..6f12f26
--- /dev/null
@@ -0,0 +1,232 @@
+/* PPPoE support library "libpppoe"
+ *
+ * Copyright 2000 Michal Ostrowski <mostrows@styx.uwaterloo.ca>,
+ *               Jamal Hadi Salim <hadi@cyberus.ca>
+ *
+ *  This program is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU General Public License
+ *  as published by the Free Software Foundation; either version
+ *  2 of the License, or (at your option) any later version.
+ */
+
+#include "pppoe.h"
+
+
+
+static int std_rcv_pado(struct session* ses,
+                       struct pppoe_packet *p_in,
+                       struct pppoe_packet **p_out){
+    
+    if( verify_packet(ses, p_in) < 0)
+       return -1;
+    
+    if(ses->state != PADO_CODE ){
+       poe_error(ses,"Unexpected packet: %P",p_in);
+       return 0;
+    }
+    
+    
+    if (DEB_DISC2) {
+       poe_dbglog (ses,"PADO received: %P", p_in);
+    }
+    
+    memcpy(&ses->remote, &p_in->addr, sizeof(struct sockaddr_ll));
+    memcpy( &ses->curr_pkt.addr, &ses->remote , sizeof(struct sockaddr_ll));
+    
+    ses->curr_pkt.hdr->code = PADR_CODE;
+    
+    /* The HOST_UNIQ has been verified already... there's no "if" about this */
+    /* if(ses->filt->htag) */
+    copy_tag(&ses->curr_pkt,get_tag(p_in->hdr,PTT_HOST_UNIQ)); 
+    
+    if (ses->filt->ntag) {
+       ses->curr_pkt.tags[TAG_AC_NAME]=NULL;
+    }
+//    copy_tag(&ses->curr_pkt,get_tag(p_in->hdr,PTT_AC_NAME));
+    
+    if(ses->filt->stag) {
+       ses->curr_pkt.tags[TAG_SRV_NAME]=NULL;
+    }
+    copy_tag(&ses->curr_pkt,get_tag(p_in->hdr,PTT_SRV_NAME));
+    
+    copy_tag(&ses->curr_pkt,get_tag(p_in->hdr,PTT_AC_COOKIE));
+    copy_tag(&ses->curr_pkt,get_tag(p_in->hdr,PTT_RELAY_SID));
+    
+    ses->state = PADS_CODE;
+    
+    ses->retransmits = 0;
+    
+    send_disc(ses, &ses->curr_pkt);
+    (*p_out) = &ses->curr_pkt;
+    
+    if (ses->np)
+       return 1;
+    
+    return 0;
+}
+
+static int std_init_disc(struct session* ses,
+                        struct pppoe_packet *p_in,
+                        struct pppoe_packet **p_out){
+    
+    memset(&ses->curr_pkt,0, sizeof(struct pppoe_packet));
+
+    
+    /* Check if already connected */
+    if( ses->state != PADO_CODE ){
+       return 1;
+    }
+    
+    ses->curr_pkt.hdr = (struct pppoe_hdr*) ses->curr_pkt.buf;
+    ses->curr_pkt.hdr->ver  = 1;
+    ses->curr_pkt.hdr->type = 1;
+    ses->curr_pkt.hdr->code = PADI_CODE;
+    
+    
+    memcpy( &ses->curr_pkt.addr, &ses->remote , sizeof(struct sockaddr_ll));
+    
+    poe_info (ses,"Sending PADI");
+    if (DEB_DISC)
+       poe_dbglog (ses,"Sending PADI");
+    
+    ses->retransmits = 0 ;
+    
+    if(ses->filt->ntag) {
+       ses->curr_pkt.tags[TAG_AC_NAME]=ses->filt->ntag;
+       poe_info(ses,"overriding AC name\n");
+    }
+    
+    if(ses->filt->stag)
+       ses->curr_pkt.tags[TAG_SRV_NAME]=ses->filt->stag;
+    
+    if(ses->filt->htag)
+       ses->curr_pkt.tags[TAG_HOST_UNIQ]=ses->filt->htag;
+    
+    send_disc(ses, &ses->curr_pkt);
+    (*p_out)= &ses->curr_pkt;
+    return 0;
+}
+
+
+static int std_rcv_pads(struct session* ses,
+                       struct pppoe_packet *p_in,
+                       struct pppoe_packet **p_out){
+    if( verify_packet(ses, p_in) < 0)
+       return -1;
+    
+    if (DEB_DISC)
+       poe_dbglog (ses,"Got connection: %x",
+                   ntohs(p_in->hdr->sid));
+    poe_info (ses,"Got connection: %x", ntohs(p_in->hdr->sid));
+    
+    ses->sp.sa_family = AF_PPPOX;
+    ses->sp.sa_protocol = PX_PROTO_OE;
+    ses->sp.sa_addr.pppoe.sid = p_in->hdr->sid;
+    memcpy(ses->sp.sa_addr.pppoe.dev,ses->name, IFNAMSIZ);
+    memcpy(ses->sp.sa_addr.pppoe.remote, p_in->addr.sll_addr, ETH_ALEN);
+    
+    
+    return 1;
+}
+
+static int std_rcv_padt(struct session* ses,
+                       struct pppoe_packet *p_in,
+                       struct pppoe_packet **p_out){
+    ses->state = PADO_CODE;
+    return 0;
+}
+
+
+extern int disc_sock;
+int client_init_ses (struct session *ses, char* devnam)
+{
+    int i=0;
+    int retval;
+    char dev[IFNAMSIZ+1];
+    int addr[ETH_ALEN];
+    int sid;
+    
+    /* do error checks here; session name etc are valid */
+//    poe_info (ses,"init_ses: creating socket");
+    
+    /* Make socket if necessary */
+    if( disc_sock < 0 ){
+       
+       disc_sock = socket(PF_PACKET, SOCK_DGRAM, 0);
+       if( disc_sock < 0 ){
+           poe_fatal(ses,
+                     "Cannot create PF_PACKET socket for PPPoE discovery\n");
+       }
+       
+    }
+    
+    /* Check for long format */
+    retval =sscanf(devnam, FMTSTRING(IFNAMSIZ),addr, addr+1, addr+2,
+                  addr+3, addr+4, addr+5,&sid,dev);
+    if( retval != 8 ){
+       /* Verify the device name , construct ses->local */
+       retval = get_sockaddr_ll(devnam,&ses->local);
+       if (retval < 0)
+           poe_fatal(ses, "client_init_ses: "
+                     "Cannot create PF_PACKET socket for PPPoE discovery\n");
+       
+       
+       ses->state = PADO_CODE;
+       memcpy(&ses->remote, &ses->local, sizeof(struct sockaddr_ll) );
+       
+       memset( ses->remote.sll_addr, 0xff, ETH_ALEN);
+    }else{
+       /* long form parsed */
+
+       /* Verify the device name , construct ses->local */
+       retval = get_sockaddr_ll(dev,&ses->local);
+       if (retval < 0)
+           poe_fatal(ses,"client_init_ses(2): "
+                     "Cannot create PF_PACKET socket for PPPoE discovery\n");
+       ses->state = PADS_CODE;
+       ses->sp.sa_family = AF_PPPOX;
+       ses->sp.sa_protocol = PX_PROTO_OE;
+       ses->sp.sa_addr.pppoe.sid = sid;
+       
+       memcpy(&ses->remote, &ses->local, sizeof(struct sockaddr_ll) );
+       
+       for(; i < ETH_ALEN ; ++i ){
+           ses->sp.sa_addr.pppoe.remote[i] = addr[i];
+           ses->remote.sll_addr[i]=addr[i];
+       }
+       memcpy(ses->sp.sa_addr.pppoe.dev, dev, IFNAMSIZ);
+       
+       
+       
+    }
+    if( retval < 0 )
+       error("bad device name: %s",devnam);
+    
+    
+    retval = bind( disc_sock ,
+                  (struct sockaddr*)&ses->local,
+                  sizeof(struct sockaddr_ll));
+    
+    
+    if( retval < 0 ){
+       error("bind to PF_PACKET socket failed: %m");
+    }
+    
+    ses->fd = socket(AF_PPPOX,SOCK_STREAM,PX_PROTO_OE);
+    if(ses->fd < 0)
+    {
+       poe_fatal(ses,"Failed to create PPPoE socket: %m");
+    }
+    
+    
+    ses->init_disc = std_init_disc;
+    ses->rcv_pado  = std_rcv_pado;
+    ses->rcv_pads  = std_rcv_pads;
+    ses->rcv_padt  = std_rcv_padt;
+    
+    /* this should be filter overridable */
+    ses->retries = 10;
+    
+    return ses->fd;
+}
+
diff --git a/pppd/plugins/pppoe/pppoe_relay.c b/pppd/plugins/pppoe/pppoe_relay.c
new file mode 100644 (file)
index 0000000..cddf257
--- /dev/null
@@ -0,0 +1,260 @@
+/* PPPoE support library "libpppoe"
+ *
+ * Copyright 2000 Michal Ostrowski <mostrows@styx.uwaterloo.ca>,
+ *               Jamal Hadi Salim <hadi@cyberus.ca>
+ *
+ *  This program is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU General Public License
+ *  as published by the Free Software Foundation; either version
+ *  2 of the License, or (at your option) any later version.
+ */
+
+#include "pppoe.h"
+
+static int relay_init_disc(struct session* ses,
+                          struct pppoe_packet *p_in,
+                          struct pppoe_packet **p_out){
+
+    ses->state = 0;
+    ses->retransmits = -1 ;
+    ses->retries = -1;
+
+    (*p_out) = NULL;
+    return 0;
+}
+
+static int pcid=0;
+static int relay_rcv_padi(struct session* ses,
+                         struct pppoe_packet *p_in,
+                         struct pppoe_packet **p_out){
+    char tag_buf[32];
+    struct pppoe_con *newpc = NULL;
+    struct pppoe_tag *tag = (struct pppoe_tag *) tag_buf;
+
+
+    tag->tag_type = PTT_RELAY_SID;
+    tag->tag_len  = htons(ETH_ALEN + sizeof(struct session *));
+
+    memcpy(tag->tag_data, p_in->addr.sll_addr, ETH_ALEN);
+    memcpy(tag->tag_data + ETH_ALEN, &ses, sizeof(struct session *));
+
+    if(! p_in->tags[TAG_RELAY_SID] ){
+       copy_tag(p_in, tag);
+    }
+
+
+    poe_dbglog(ses, "Recv'd PADI: %P",p_in);
+    poe_dbglog(ses, "Recv'd packet: %P",p_in);
+    newpc = get_con( ntohs(tag->tag_len), tag->tag_data );
+    if(!newpc){
+       
+       newpc = (struct pppoe_con *) malloc(sizeof(struct pppoe_con));
+       memset(newpc , 0, sizeof(struct pppoe_con));
+       
+       newpc->id = pcid++;
+       
+       newpc->key_len = ntohs(p_in->tags[TAG_RELAY_SID]->tag_len);
+       memcpy(newpc->key, p_in->tags[TAG_RELAY_SID]->tag_data, newpc->key_len);
+       memcpy(newpc->client, p_in->addr.sll_addr, ETH_ALEN);
+       
+       memcpy(newpc->server, MAC_BCAST_ADDR, ETH_ALEN);
+       
+       store_con(newpc);
+       
+    }
+
+    ++newpc->ref_count;
+
+    memset(p_in->addr.sll_addr, 0xff, ETH_ALEN);
+
+    p_in->addr.sll_ifindex = ses->remote.sll_ifindex;
+
+    send_disc(ses, p_in);
+    return 0;
+}
+
+static int relay_rcv_pkt(struct session* ses,
+                        struct pppoe_packet *p_in,
+                        struct pppoe_packet **p_out){
+    struct pppoe_con *pc;
+    char tag_buf[32];
+    struct pppoe_tag *tag = p_in->tags[TAG_RELAY_SID];
+
+    if( !tag ) return 0;
+
+    pc = get_con(ntohs(tag->tag_len),tag->tag_data);
+
+    if( !pc ) return 0;
+
+    poe_dbglog(ses, "Recv'd packet: %P",p_in);
+
+    if( memcmp(pc->client , p_in->addr.sll_addr , ETH_ALEN ) == 0 ){
+       
+       memcpy(p_in->addr.sll_addr, pc->server, ETH_ALEN);
+       p_in->addr.sll_ifindex = ses->remote.sll_ifindex;
+       
+    }else{
+       if( memcmp(pc->server, MAC_BCAST_ADDR, ETH_ALEN) == 0 ){
+           memcpy(pc->server, p_in->addr.sll_addr, ETH_ALEN);
+       
+       }else if( memcmp(pc->server, p_in->addr.sll_addr, ETH_ALEN) !=0){
+           return 0;
+       }
+       
+       memcpy(p_in->addr.sll_addr, pc->client, ETH_ALEN);
+       p_in->addr.sll_ifindex = ses->local.sll_ifindex;
+       
+       
+    }
+
+
+    send_disc(ses, p_in);
+    return 0;
+}
+
+static int relay_rcv_pads(struct session* ses,
+                         struct pppoe_packet *p_in,
+                         struct pppoe_packet **p_out){
+
+    struct pppoe_con *pc;
+    char tag_buf[32];
+    struct pppoe_tag *tag = p_in->tags[TAG_RELAY_SID];
+    struct sockaddr_pppox sp_cl= { AF_PPPOX, PX_PROTO_OE,
+                                  { p_in->hdr->sid, {0,},{0,}}};
+
+    struct sockaddr_pppox sp_sv= { AF_PPPOX, PX_PROTO_OE,
+                                  { p_in->hdr->sid, {0,},{0,}}};
+
+    int ret;
+
+
+    if( !tag ) return 0;
+
+    pc = get_con(ntohs(tag->tag_len),tag->tag_data);
+
+    if( !pc ) return 0;
+
+
+    if(!pc->connected){
+       
+       pc->sv_sock = socket( AF_PPPOX, SOCK_DGRAM, PX_PROTO_OE);
+       if( pc->sv_sock < 0){
+           poe_fatal(ses,"Cannot open PPPoE socket: %i",errno);
+       }
+       
+       pc->cl_sock = socket( AF_PPPOX, SOCK_DGRAM, PX_PROTO_OE);
+       if( pc->cl_sock < 0){
+           poe_fatal(ses,"Cannot open PPPoE socket: %i",errno);
+       }
+       
+       memcpy( sp_sv.sa_addr.pppoe.dev, ses->fwd_name, IFNAMSIZ);
+       memcpy( sp_sv.sa_addr.pppoe.remote, pc->server, ETH_ALEN);
+       
+       ret = connect( pc->sv_sock,
+                      (struct sockaddr*)&sp_sv,
+                      sizeof(struct sockaddr_pppox));
+       if( ret < 0){
+           poe_fatal(ses,"Cannot connect PPPoE socket: %i",errno);
+       }
+       
+       memcpy( sp_cl.sa_addr.pppoe.dev, ses->name, IFNAMSIZ);
+       memcpy( sp_cl.sa_addr.pppoe.remote, pc->client, ETH_ALEN);
+       
+       ret = connect( pc->cl_sock,
+                      (struct sockaddr*)&sp_cl,
+                      sizeof(struct sockaddr_pppox));
+       if( ret < 0){
+           poe_fatal(ses,"Cannot connect PPPoE socket: %i",errno);
+       }
+       
+       
+       ret = ioctl( pc->sv_sock, PPPOEIOCSFWD, &sp_cl);
+       if( ret < 0){
+           poe_fatal(ses,"Cannot set forwarding on PPPoE socket: %i",errno);
+       }
+       
+       ret = ioctl( pc->cl_sock, PPPOEIOCSFWD, &sp_sv);
+       if( ret < 0){
+           poe_fatal(ses,"Cannot set forwarding on PPPoE socket: %i",errno);
+       }
+       
+       pc->connected = 1;
+    }
+
+    poe_info(ses,"PPPoE relay for %E established to %E (sid=%04x)\n",
+            pc->client,pc->server, p_in->hdr->sid);
+
+    return relay_rcv_pkt(ses,p_in,p_out);
+}
+
+
+static int relay_rcv_padt(struct session* ses,
+                         struct pppoe_packet *p_in,
+                         struct pppoe_packet **p_out){
+
+    int ret;
+    struct pppoe_con *pc;
+    char tag_buf[32];
+    struct pppoe_tag *tag = p_in->tags[TAG_RELAY_SID];
+
+    if( !tag ) return 0;
+
+    pc = get_con(ntohs(tag->tag_len),tag->tag_data);
+
+    if( !pc ) return 0;
+
+    ret = relay_rcv_pkt(ses,p_in,p_out);
+
+
+    if(pc->cl_sock>0){
+       close(pc->cl_sock);
+    }
+
+    if(pc->sv_sock>0){
+       close(pc->sv_sock);
+    }
+
+    --pc->ref_count;
+    if( pc->ref_count == 0 ){
+       delete_con(pc->key_len, pc->key);
+       
+       free(pc);
+    }
+}
+
+
+int relay_init_ses(struct session *ses, char* from, char* to)
+{
+    int retval = client_init_ses(ses, from);
+
+    if(retval<0) return retval;
+
+    ses->fwd_sock =  socket(PF_PACKET, SOCK_DGRAM, 0);
+    if( ses->fwd_sock < 0 ) {
+       poe_fatal(ses,"Cannot create PF_PACKET socket for PPPoE forwarding\n");
+    }
+
+    /* Verify the device name , construct ses->local */
+    retval = get_sockaddr_ll(to, &ses->remote);
+    if (retval < 0)
+       poe_fatal(ses,"relay_init_ses:get_sockaddr_ll failed %m");
+
+    retval = bind( ses->fwd_sock ,
+                  (struct sockaddr*)&ses->remote,
+                  sizeof(struct sockaddr_ll));
+
+    if( retval < 0 ){
+       poe_fatal(ses,"bind to PF_PACKET socket failed: %m");
+    }
+
+    memcpy(ses->fwd_name, to, IFNAMSIZ);
+    memcpy(ses->name, from, IFNAMSIZ);
+
+
+    ses->init_disc = relay_init_disc;
+    ses->rcv_padi  = relay_rcv_padi;
+    ses->rcv_pado  = relay_rcv_pkt;
+    ses->rcv_padr  = relay_rcv_pkt;
+    ses->rcv_pads  = relay_rcv_pads;
+    ses->rcv_padt  = relay_rcv_padt;
+}
diff --git a/pppd/plugins/pppoe/pppoe_server.c b/pppd/plugins/pppoe/pppoe_server.c
new file mode 100644 (file)
index 0000000..ff7a5ae
--- /dev/null
@@ -0,0 +1,143 @@
+/* PPPoE support library "libpppoe"
+ *
+ * Copyright 2000 Michal Ostrowski <mostrows@styx.uwaterloo.ca>,
+ *               Jamal Hadi Salim <hadi@cyberus.ca>
+ *
+ *  This program is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU General Public License
+ *  as published by the Free Software Foundation; either version
+ *  2 of the License, or (at your option) any later version.
+ */
+#include "pppoe.h"
+#include <unistd.h>
+
+static unsigned int pcid=1111;
+static int srv_rcv_padi(struct session* ses, 
+                       struct pppoe_packet *p_in,
+                       struct pppoe_packet **p_out){
+    struct pppoe_con *newpc = NULL;
+    struct pppoe_tag *tag;
+    
+    poe_dbglog(ses,"Srv Recv'd packet: %P\n",p_in);
+    
+    
+    ses->curr_pkt.hdr = (struct pppoe_hdr*) ses->curr_pkt.buf;
+    ses->curr_pkt.hdr->ver  = 1;
+    ses->curr_pkt.hdr->type = 1;
+
+    tag = get_tag(p_in->hdr,PTT_SRV_NAME);
+
+    if(!tag )
+       return 0;
+
+    if( ntohs(tag->tag_len)==0 ){
+       ses->curr_pkt.tags[TAG_SRV_NAME] = ses->filt->stag ;
+    }else if( tag->tag_len != ses->filt->stag->tag_len
+             || !memcmp( tag+1, ses->filt->stag, ntohs(tag->tag_len)) ){
+       return 0;
+    }else{
+       ses->curr_pkt.tags[TAG_SRV_NAME] = tag;
+    }
+
+    ses->curr_pkt.tags[ TAG_AC_NAME] = ses->filt->ntag ;
+    ses->curr_pkt.tags[ TAG_HOST_UNIQ ] = get_tag(p_in->hdr,PTT_HOST_UNIQ);
+    
+    memcpy(&ses->remote, &p_in->addr, sizeof(struct sockaddr_ll));
+    memcpy( &ses->curr_pkt.addr, &ses->remote , sizeof(struct sockaddr_ll));
+    
+    ses->curr_pkt.hdr->code =  PADO_CODE;
+    
+    
+    ses->curr_pkt.tags[ TAG_RELAY_SID ] = get_tag(p_in->hdr,PTT_RELAY_SID);
+
+    send_disc(ses, &ses->curr_pkt);
+    poe_dbglog(ses,"Srv Sent packet: %P\n",&ses->curr_pkt);
+    
+    return 0;
+}
+
+
+static int srv_rcv_padr(struct session* ses, 
+                       struct pppoe_packet *p_in,
+                       struct pppoe_packet **p_out){
+    struct pppoe_tag *tag;
+
+    poe_dbglog(ses,"Recv'd packet: %P\n",p_in);
+    
+
+
+    /* Run checks to ensure this packets asks for 
+       what we're willing to offer */
+
+    tag = get_tag(p_in->hdr,PTT_SRV_NAME);
+
+    if(!tag || tag->tag_len == 0 ){
+       p_in->tags[TAG_SRV_NAME] = ses->filt->stag;
+
+    }else if( tag->tag_len != ses->filt->stag->tag_len
+            || !memcmp(tag + 1 , ses->filt->stag, ntohs(tag->tag_len)) ){
+       return 0;
+    }else{
+       p_in->tags[TAG_SRV_NAME] = tag;
+    }
+
+    tag = get_tag(p_in->hdr,PTT_AC_NAME);
+    if( !tag || tag->tag_len==0 ){
+       p_in->tags[TAG_AC_NAME] = ses->filt->ntag;
+    }else if( tag->tag_len != ses->filt->ntag->tag_len
+         || !memcmp(tag + 1, ses->filt->ntag, ntohs(tag->tag_len)) ){
+       return 0;
+    }else{
+       p_in->tags[TAG_AC_NAME] = tag;
+    }
+
+    
+    
+    
+    pcid = ++pcid & 0x0000ffff ;
+    if(pcid == 0 ){
+       pcid = 1111;
+    }
+    
+    p_in->hdr->sid  = ntohs(pcid);
+    
+    p_in->hdr->code = PADS_CODE;
+    send_disc(ses, p_in);
+    
+    poe_dbglog(ses,"Sent packet: %P\n",p_in);
+    
+    ses->sp.sa_family = AF_PPPOX;
+    ses->sp.sa_protocol=PX_PROTO_OE;
+    ses->sp.sa_addr.pppoe.sid = p_in->hdr->sid;
+    memcpy(ses->sp.sa_addr.pppoe.dev, ses->name, IFNAMSIZ);
+    memcpy(ses->sp.sa_addr.pppoe.remote, p_in->addr.sll_addr, ETH_ALEN);
+    memcpy(&ses->remote, &p_in->addr, sizeof(struct sockaddr_ll));
+    return 1;
+}
+
+static int srv_rcv_padt(struct session* ses, 
+                       struct pppoe_packet *p_in,
+                       struct pppoe_packet **p_out){
+    return 0;
+}
+
+
+
+int srv_init_ses(struct session *ses, char* from)
+{
+    int retval;
+    retval = client_init_ses(ses, from);
+    ses->init_disc = NULL;
+    ses->rcv_pado  = NULL;
+    ses->rcv_pads  = NULL;
+    ses->rcv_padi  = srv_rcv_padi;
+    ses->rcv_padr  = srv_rcv_padr;
+    ses->rcv_padt  = srv_rcv_padt;
+
+    /* retries forever */
+    ses->retries   = -1;
+
+    return retval;
+}
+
+
diff --git a/pppd/plugins/pppoe/pppoed.c b/pppd/plugins/pppoe/pppoed.c
new file mode 100644 (file)
index 0000000..98079be
--- /dev/null
@@ -0,0 +1,283 @@
+/* PPPoE support library "libpppoe"
+ *
+ * Copyright 2000 Jamal Hadi Salim <hadi@cyberus.ca>
+ *
+ *  This program is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU General Public License
+ *  as published by the Free Software Foundation; either version
+ *  2 of the License, or (at your option) any later version.
+ */
+
+#include "pppoe.h"
+
+int detached=1;
+void
+sigproc (int src)
+{
+    int i;
+    fprintf (stderr,"Received signal %d", src);
+}
+
+void
+sigchild (int src)
+{
+    pid_t pid;
+    int status;
+    int i;
+    pid = waitpid (-1, &status, WNOHANG);
+    
+    if (!detached)
+       fprintf (stderr,"Child received signal %d PID %d, status %d", src, pid, status);
+    if (pid < 1) {
+       return;
+    }
+}
+
+void
+print_help ()
+{
+    
+    fprintf (stdout,"\npppoe version %d.%d build %d", VERSION_MAJOR, VERSION_MINOR,
+            VERSION_DATE);
+    fprintf (stdout,"\nrecognized options are:");
+    fprintf (stdout,"\n -I <interface> : overrides the default interface of eth0");
+    fprintf (stdout,"\n -S : starts pppoed in server mode");
+    fprintf (stdout,"\n -R <num_retries>: forces pppoed to be restarted num_retries");
+    fprintf (stdout,"\n                   should the other end be detected to be dead.");
+    fprintf (stdout,"\n                   Needs lcp_echo. Read the INSTALL file instructions");
+    fprintf (stdout,"\n -F <filename> : specifies additional ppp options file");
+    fprintf (stdout,"\n -C <filename> : ppp options file in /etc/ppp/peers/");
+    fprintf (stdout,"\n -d <level> : sets debug level");
+    fprintf (stdout,"\n -D : prevents pppoed from detaching itself and running in the background");
+    fprintf (stdout,"\n -P <path to pppd> : selects a different pppd. Defaults to " _PATH_PPPD);
+    fprintf (stdout,"\n -A <AC name> to select a specific AC by name");
+    fprintf (stdout,"\n -E <AC service name> to select a specific AC service by name");
+    fprintf (stdout,"\n -G Do service discovery only");
+    fprintf (stdout,"\n -H Do service discovery and connection (no pppd)\n");
+}
+
+
+int 
+get_args (int argc, char **argv,struct session *sess)
+{
+    struct filter *filt;
+    struct host_tag *tg;
+    int opt;
+    
+
+    sess->opt_debug = 0;
+    DEB_DISC=0;
+    DEB_DISC2=0;
+    sess->log_to_fd = 1;
+    sess->np = 0;
+    sess->opt_daemonize = 0;
+    
+    sess->log_to_fd = fileno (stdout);
+    
+/* defaults to eth0 */
+    strcpy (sess->name, "eth0");
+    
+    
+    if ((sess->filt=malloc(sizeof(struct filter))) == NULL) {
+        poe_error (sess,"failed to malloc for Filter ");
+        poe_die (-1);
+    }
+    
+    filt=sess->filt;  /* makes the code more readable */
+    memset(filt,0,sizeof(struct filter));
+    
+    filt->num_restart=1;
+    
+/* set default filters; move this to routine */
+    /* parse options */
+    
+    while ((opt = getopt (argc, argv, "A:C:E:d:DR:I:F:L:V:P:SN:GH")) != -1)
+       
+       switch (opt) {
+       case 'R':                       /* sets number of retries */
+           filt->num_restart = strtol (optarg, (char **) NULL, 10);
+           filt->num_restart += 1;
+           break;
+       case 'I':                       /* sets interface */
+           if (strlen (optarg) >= IFNAMSIZ) {
+               poe_error (sess,"interface name cannot exceed %d characters", IFNAMSIZ - 1);
+               return (-1);
+           }
+           strncpy (sess->name, optarg, strlen(optarg)+1);
+           break;
+       case 'C':                       /* name of the file in /etc/ppp/peers */
+           if (NULL != filt->fname) {
+               poe_error (sess,"-F can not be used with -C");
+               return (-1);
+           }
+           if (strlen(optarg) > MAX_FNAME) {
+               poe_error (sess,"file name cannot exceed %d characters", MAX_FNAME - 1);
+               return (-1);
+           }
+           filt->fname=malloc(strlen(optarg));
+           strncpy (filt->fname, optarg, strlen(optarg));
+           filt->peermode=1;
+           break;
+       case 'F':                       /* sets the options file */
+           if (NULL != filt->fname) {
+               poe_error (sess,"-F can not be used with -C");
+               return (-1);
+           }
+           
+           if (strlen(optarg) > MAX_FNAME) {
+               poe_error (sess,"file name cannot exceed %d characters", MAX_FNAME - 1);
+               return (-1);
+           }
+           filt->fname=malloc(strlen(optarg)+1);
+           strncpy (filt->fname, optarg, strlen(optarg)+1);
+           
+           poe_info (sess,"selected %s as filename\n",filt->fname);
+           break;
+       case 'D':                       /* don't daemonize */
+           sess->opt_daemonize = 1;
+           detached=0;
+           break;
+       case 'd':                       /* debug level */
+           sess->opt_debug = strtol (optarg, (char **) NULL, 10);
+           if (sess->opt_debug & 0x0002)
+               DEB_DISC=1;
+           if (sess->opt_debug & 0x0004)
+               DEB_DISC2=1;
+           break;
+       case 'P':                       /* sets the pppd binary */
+           if (strlen(optarg) > MAX_FNAME) {
+               poe_error (sess,"pppd binary cant exceed %d characters", MAX_FNAME - 1);
+               return (-1);
+           }
+           filt->pppd=malloc(strlen(optarg));
+           strncpy (filt->pppd, optarg, strlen(optarg));
+           break;
+       case 'H':                       
+           sess->np = 2;
+           break;
+       case 'G':                       
+           sess->np = 1;
+           break;
+       case 'V':                       /* version */
+           fprintf (stdout,"pppoe version %d.%d build %d", VERSION_MAJOR,
+                    VERSION_MINOR, VERSION_DATE);
+           return (0);
+       case 'S':                       /* server mode */
+           sess->type = SESSION_SERVER;
+           break;
+       case 'A':                       /* AC override */
+           poe_info (sess,"AC name override to %s", optarg);
+           if (strlen (optarg) > 255) {
+               poe_error (sess," AC name too long 
+                         (maximum allowed 256 chars)");
+               poe_die(-1);
+           }
+           if ((sess->filt->ntag= malloc (sizeof (struct pppoe_tag) + 
+                                          strlen (optarg)))== NULL) {
+               poe_error (sess,"failed to malloc for AC name");
+               poe_die(-1);
+           }
+           sess->filt->ntag->tag_len=htons(strlen(optarg));
+           sess->filt->ntag->tag_type=PTT_AC_NAME;
+           poe_error (sess," pppoe_ac_name: AC name Override %p\n",
+                      sess->filt->ntag);
+           strcpy(sess->filt->ntag->tag_data,optarg);
+           break;
+       case 'E':                       /* AC service name override */
+           poe_info (sess,"AC service name override to %s", optarg);
+           if (strlen (optarg) > 255) {
+               poe_error (sess," Service name too long 
+                 (maximum allowed 256 chars)");
+               poe_die(-1);
+           }
+           
+           if ((filt->stag = malloc (strlen (optarg) + sizeof (struct pppoe_tag))) == NULL) {
+               poe_error (sess,"failed to malloc for service name: %m");
+               return (-1);
+           }
+           
+           filt->stag->tag_len = htons (strlen (optarg));
+           filt->stag->tag_type = PTT_SRV_NAME;
+           strcpy ((char *) (filt->stag->tag_data), optarg);
+           break;
+       default:
+           poe_error (sess,"Unknown option '%c'", optopt);
+           print_help ();
+           return (-1);
+       }
+    
+    
+    return (1);
+    
+}
+
+
+int main(int argc, char** argv){
+    int ret;
+    struct filter *filt;
+    struct session *ses = (struct session *)malloc(sizeof(struct session));
+    char buf[256];
+    ses=(void *)malloc(sizeof(struct session));
+    
+    if(!ses){
+       return -1;
+    }
+    memset(ses,0,sizeof(struct session));
+    
+    
+    
+    openlog ("pppoed", LOG_PID | LOG_NDELAY, LOG_PPPOE);
+    setlogmask (LOG_UPTO (ses->opt_debug ? LOG_DEBUG : LOG_INFO));
+    
+    
+    if ((get_args (argc,(char **) argv,ses)) <1)
+        poe_die(-1);
+    
+    filt=ses->filt;  /* makes the code more readable */
+    
+    if (!ses->np) {
+       poe_create_pidfile (ses);
+//     signal (SIGINT, &sigproc);
+//     signal (SIGTERM, &sigproc);
+       signal (SIGCHLD, &sigchild);
+    }
+    
+    if(ses->type == SESSION_CLIENT){
+
+       poe_info(ses,"calling client_init_ses\n");
+       ret = client_init_ses(ses,ses->name);
+    
+       if( ret < 0 ){
+           return -1;
+       }
+
+       while (ses->filt->num_restart > 0)
+       {
+           poe_info(ses,"Restart number %d ",ses->filt->num_restart);
+           ppp_connect (ses);
+           ses->filt->num_restart--;
+       }
+
+    }else if( ses->type == SESSION_SERVER ){
+
+       poe_info(ses,"calling srv_init_ses\n");
+       ret = srv_init_ses(ses,ses->name);
+
+       if( ret < 0 ){
+           return -1;
+       }
+
+       ret = 1;
+       while(ret>=0)
+           ret = ppp_connect(ses);
+    
+    }
+
+    
+    
+    
+    poe_info(ses,"ppp_connect came back! %d",ret);
+    
+    exit(0);
+    
+}
diff --git a/pppd/plugins/pppoe/pppoefwd.c b/pppd/plugins/pppoe/pppoefwd.c
new file mode 100644 (file)
index 0000000..7178766
--- /dev/null
@@ -0,0 +1,61 @@
+#include "pppoe.h"
+
+void fatal (char *fmt, ...)
+{
+    va_list pvar;
+
+#if defined(__STDC__)
+    va_start(pvar, fmt);
+#else
+    char *fmt;
+    va_start(pvar);
+    fmt = va_arg(pvar, char *);
+#endif
+
+    vprintf( fmt, pvar);
+    va_end(pvar);
+
+    exit(1);                   /* as promised */
+}
+
+void info (char *fmt, ...)
+{
+    va_list pvar;
+
+#if defined(__STDC__)
+    va_start(pvar, fmt);
+#else
+    char *fmt;
+    va_start(pvar);
+    fmt = va_arg(pvar, char *);
+#endif
+
+    vprintf( fmt, pvar);
+    va_end(pvar);
+
+}
+
+
+int main(int argc, char** argv){
+    int ret;
+    struct session *ses = (struct session *)malloc(sizeof(struct session));
+
+    if(!ses) return -1;
+
+    ret = relay_init_ses(ses,argv[1],argv[2]);
+    
+    if( ret < 0 ){
+       return -1;
+    }
+
+    ses->log_to_fd = 1;
+    ses->opt_debug=1;
+    while(1)
+       ret = session_connect(ses);
+    
+    
+    
+    return ret;
+
+
+}
diff --git a/pppd/plugins/pppoe/pppoehash.c b/pppd/plugins/pppoe/pppoehash.c
new file mode 100644 (file)
index 0000000..12ac069
--- /dev/null
@@ -0,0 +1,91 @@
+/* PPPoE support library "libpppoe"
+ *
+ * Copyright 2000 Michal Ostrowski <mostrows@styx.uwaterloo.ca>,
+ *               Jamal Hadi Salim <hadi@cyberus.ca>
+ *
+ *  This program is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU General Public License
+ *  as published by the Free Software Foundation; either version
+ *  2 of the License, or (at your option) any later version.
+ */
+#include "pppoe.h"
+
+
+#define PPPOE_HASH_SIZE 16
+
+
+static inline int keycmp(char *a, char *b, int x, int y){
+    return x==y && !memcmp(a,b,x);
+}
+
+static int hash_con(int key_len, char* key)
+{
+    int i = 0;
+    char hash[sizeof(int)]={0,};
+
+    for (i = 0; i < key_len ; ++i)
+       hash[i% sizeof(int)] = hash[i%sizeof(int)] ^ key[i];
+
+    i = (*((int*)hash)) ;
+    i &= PPPOE_HASH_SIZE - 1;
+
+    return i;
+}      
+
+static struct pppoe_con *con_ht[PPPOE_HASH_SIZE] = { 0, };
+
+struct pppoe_con *get_con(int len, char *key)
+{
+    int hash = hash_con(len, key);
+    struct pppoe_con *ret;
+
+    ret = con_ht[hash];
+
+    while (ret && !keycmp(ret->key,key, ret->key_len, len))
+       ret = ret->next;
+
+    return ret;
+}
+
+int store_con(struct pppoe_con *pc)
+{
+    int hash = hash_con(pc->key_len, pc->key);
+    struct pppoe_con *ret;
+
+    ret = con_ht[hash];
+    while (ret) {
+       if (!keycmp(ret->key, pc->key, ret->key_len, pc->key_len))
+           return -EALREADY;
+       
+       ret = ret->next;
+    }
+
+    if (!ret) {
+       pc->next = con_ht[hash];
+       con_ht[hash] = pc;
+    }
+
+    return 0;
+}
+
+struct pppoe_con *delete_con(unsigned long len, char *key)
+{
+    int hash = hash_con(len, key);
+    struct pppoe_con *ret, **src;
+
+    ret = con_ht[hash];
+    src = &con_ht[hash];
+
+    while (ret) {
+       if (keycmp(ret->key,key, ret->key_len, len)) {
+           *src = ret->next;
+           break;
+       }
+       
+       src = &ret->next;
+       ret = ret->next;
+    }
+
+    return ret;
+}
+
diff --git a/pppd/plugins/pppoe/utils.c b/pppd/plugins/pppoe/utils.c
new file mode 100644 (file)
index 0000000..e6751c9
--- /dev/null
@@ -0,0 +1,667 @@
+
+/*
+ * utils.c - various utility functions used in pppoed.
+ *
+ * mostly stolen from ppp-2.3.10 by Marc Boucher <marc@mbsi.ca>
+ *
+ * Feb 18/2000 Made fully re-entrant (JHS)
+ *
+ * Copyright (c) 1999 The Australian National University.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright poe_notice and this paragraph are
+ * duplicated in all such forms and that any documentation,
+ * advertising materials, and other materials related to such
+ * distribution and use acknowledge that the software was developed
+ * by the Australian National University.  The name of the University
+ * may not be used to endorse or promote products derived from this
+ * software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+#include <stdio.h>             /* stdio               */
+#include <stdlib.h>            /* strtoul(), realloc() */
+#include <string.h>            /* memcpy()             */
+#include <unistd.h>            /* STDIN_FILENO,exec    */
+#include <errno.h>             /* errno                */
+
+#include <sys/time.h>
+
+#include <net/ethernet.h>
+#include <netinet/in.h>
+
+#include <stdarg.h>
+#include <ctype.h>
+#include <syslog.h>
+#include <limits.h>
+#include <paths.h>
+
+#include "pppoe.h"
+
+static char pidfilename[PATH_MAX];     /* name of pid file */
+
+/*
+static int detached = 0;
+   log_to_fd = -1;
+ */
+
+static void vslp_printer (void *, char *,...);
+static void format_packet (struct pppoe_packet *, int, void (*)(void *, char *,...), void *);
+static void format_tag (struct pppoe_tag *, void (*)(void *, char *,...), void *);
+struct buffer_poe_info {
+  char *ptr;
+  int len;
+};
+
+void poe_die (int status);
+
+
+/*
+ * vpoe_slprintf - like vsprintf, except we
+ * also specify the length of the output buffer, and we handle
+ * %r (recursive format), %m (poe_error message), %v (visible string),
+ * %q (quoted string), %t (current time) and %E (Ether address) formats.
+ * Doesn't do floating-point formats.
+ * Returns the number of chars put into buf.
+ */
+#define OUTCHAR(c)     (buflen > 0? (--buflen, *buf++ = (c)): 0)
+
+int
+vpoe_slprintf (char *buf, int buflen, char *fmt, va_list args)
+{
+  int c, i, n;
+  int width, prec, fillch;
+  int base, len, neg, quoted;
+  unsigned long val = 0;
+  char *str, *f, *buf0;
+  unsigned char *p;
+  char num[32];
+  time_t t;
+  static char hexchars[] = "0123456789abcdef";
+  struct buffer_poe_info bufpoe_info;
+
+  buf0 = buf;
+  --buflen;
+  while (buflen > 0) {
+    for (f = fmt; *f != '%' && *f != 0; ++f);
+    if (f > fmt) {
+      len = f - fmt;
+      if (len > buflen)
+       len = buflen;
+      memcpy (buf, fmt, len);
+      buf += len;
+      buflen -= len;
+      fmt = f;
+    }
+    if (*fmt == 0)
+      break;
+    c = *++fmt;
+    width = 0;
+    prec = -1;
+    fillch = ' ';
+    if (c == '0') {
+      fillch = '0';
+      c = *++fmt;
+    }
+    if (c == '*') {
+      width = va_arg (args, int);
+      c = *++fmt;
+    }
+    else {
+      while (isdigit (c)) {
+       width = width * 10 + c - '0';
+       c = *++fmt;
+      }
+    }
+    if (c == '.') {
+      c = *++fmt;
+      if (c == '*') {
+       prec = va_arg (args, int);
+       c = *++fmt;
+      }
+      else {
+       prec = 0;
+       while (isdigit (c)) {
+         prec = prec * 10 + c - '0';
+         c = *++fmt;
+       }
+      }
+    }
+    str = 0;
+    base = 0;
+    neg = 0;
+    ++fmt;
+    switch (c) {
+    case 'd':
+      i = va_arg (args, int);
+      if (i < 0) {
+       neg = 1;
+       val = -i;
+      }
+      else
+       val = i;
+      base = 10;
+      break;
+    case 'o':
+      val = va_arg (args, unsigned int);
+      base = 8;
+      break;
+    case 'x':
+    case 'X':
+      val = va_arg (args, unsigned int);
+      base = 16;
+      break;
+    case 'p':
+      val = (unsigned long) va_arg (args, void *);
+      base = 16;
+      neg = 2;
+      break;
+    case 's':
+      str = va_arg (args, char *);
+      break;
+    case 'c':
+      num[0] = va_arg (args, int);
+      num[1] = 0;
+      str = num;
+      break;
+    case 'm':
+      str = strerror (errno);
+      break;
+    case 'E':
+      p = va_arg (args, unsigned char *);
+      for (n = ETH_ALEN; n > 0; --n) {
+       c = *p++;
+       OUTCHAR (hexchars[(c >> 4) & 0xf]);
+       OUTCHAR (hexchars[c & 0xf]);
+       if (n > 1)
+         OUTCHAR (':');
+      }
+      continue;
+    case 'r':
+      f = va_arg (args, char *);
+#ifndef __powerpc__
+      n = vpoe_slprintf (buf, buflen + 1, f, va_arg (args, va_list));
+#else
+      /* On the powerpc, a va_list is an array of 1 structure */
+      n = vpoe_slprintf (buf, buflen + 1, f, va_arg (args, void *));
+#endif
+      buf += n;
+      buflen -= n;
+      continue;
+    case 't':
+      time (&t);
+      str = ctime (&t);
+      str += 4;                        /* chop off the day name */
+      str[15] = 0;             /* chop off year and newline */
+      break;
+    case 'v':                  /* "visible" string */
+    case 'q':                  /* quoted string */
+      quoted = c == 'q';
+      p = va_arg (args, unsigned char *);
+      if (fillch == '0' && prec >= 0) {
+       n = prec;
+      }
+      else {
+       n = strlen ((char *) p);
+       if (prec >= 0 && n > prec)
+         n = prec;
+      }
+      while (n > 0 && buflen > 0) {
+       c = *p++;
+       --n;
+       if (!quoted && c >= 0x80) {
+         OUTCHAR ('M');
+         OUTCHAR ('-');
+         c -= 0x80;
+       }
+       if (quoted && (c == '"' || c == '\\'))
+         OUTCHAR ('\\');
+       if (c < 0x20 || (0x7f <= c && c < 0xa0)) {
+         if (quoted) {
+           OUTCHAR ('\\');
+           switch (c) {
+           case '\t':
+             OUTCHAR ('t');
+             break;
+           case '\n':
+             OUTCHAR ('n');
+             break;
+           case '\b':
+             OUTCHAR ('b');
+             break;
+           case '\f':
+             OUTCHAR ('f');
+             break;
+           default:
+             OUTCHAR ('x');
+             OUTCHAR (hexchars[c >> 4]);
+             OUTCHAR (hexchars[c & 0xf]);
+           }
+         }
+         else {
+           if (c == '\t')
+             OUTCHAR (c);
+           else {
+             OUTCHAR ('^');
+             OUTCHAR (c ^ 0x40);
+           }
+         }
+       }
+       else
+         OUTCHAR (c);
+      }
+      continue;
+    case 'P':                  /* print PPPoE packet */
+      bufpoe_info.ptr = buf;
+      bufpoe_info.len = buflen + 1;
+      p = va_arg (args, unsigned char *);
+      n = va_arg (args, int);
+      format_packet ((struct pppoe_packet *) p, n, vslp_printer, &bufpoe_info);
+      buf = bufpoe_info.ptr;
+      buflen = bufpoe_info.len - 1;
+      continue;
+    case 'T':                  /* print PPPoE tag */
+      bufpoe_info.ptr = buf;
+      bufpoe_info.len = buflen + 1;
+      p = va_arg (args, unsigned char *);
+      format_tag ((struct pppoe_tag *) p, vslp_printer, &bufpoe_info);
+      buf = bufpoe_info.ptr;
+      buflen = bufpoe_info.len - 1;
+      continue;
+    case 'B':
+      p = va_arg (args, unsigned char *);
+      for (n = prec; n > 0; --n) {
+       c = *p++;
+       if (fillch == ' ')
+         OUTCHAR (' ');
+       OUTCHAR (hexchars[(c >> 4) & 0xf]);
+       OUTCHAR (hexchars[c & 0xf]);
+      }
+      continue;
+    default:
+      *buf++ = '%';
+      if (c != '%')
+       --fmt;                  /* so %z outputs %z etc. */
+      --buflen;
+      continue;
+    }
+    if (base != 0) {
+      str = num + sizeof (num);
+      *--str = 0;
+      while (str > num + neg) {
+       *--str = hexchars[val % base];
+       val = val / base;
+       if (--prec <= 0 && val == 0)
+         break;
+      }
+      switch (neg) {
+      case 1:
+       *--str = '-';
+       break;
+      case 2:
+       *--str = 'x';
+       *--str = '0';
+       break;
+      }
+      len = num + sizeof (num) - 1 - str;
+    }
+    else {
+      len = strlen (str);
+      if (prec >= 0 && len > prec)
+       len = prec;
+    }
+    if (width > 0) {
+      if (width > buflen)
+       width = buflen;
+      if ((n = width - len) > 0) {
+       buflen -= n;
+       for (; n > 0; --n)
+         *buf++ = fillch;
+      }
+    }
+    if (len > buflen)
+      len = buflen;
+    memcpy (buf, str, len);
+    buf += len;
+    buflen -= len;
+  }
+  *buf = 0;
+  return buf - buf0;
+}
+
+/*
+ * vslp_printer - used in processing a %P format
+ */
+static void
+vslp_printer (void *arg, char *fmt,...)
+{
+  int n;
+  va_list pvar;
+  struct buffer_poe_info *bi;
+
+  va_start (pvar, fmt);
+
+  bi = (struct buffer_poe_info *) arg;
+  n = vpoe_slprintf (bi->ptr, bi->len, fmt, pvar);
+  va_end (pvar);
+
+  bi->ptr += n;
+  bi->len -= n;
+}
+
+/*
+ * format_packet - make a readable representation of a packet,
+ * calling `printer(arg, format, ...)' to output it.
+ */
+static void
+format_packet (struct pppoe_packet *p,
+              int len,
+              void (*printer) (void *, char *,...),
+              void *arg)
+{
+  struct pppoe_tag *t;
+
+  printer (arg, "Ether addr: %E\n", p->addr.sll_addr);
+
+  switch ((unsigned) ntohs (p->addr.sll_protocol)) {
+  case ETH_P_PPPOE_DISC:
+    printer (arg, " (PPPOE Discovery)\n");
+    break;
+  case ETH_P_PPPOE_SESS:
+    printer (arg, " (PPPOE Session)\n");
+    break;
+  }
+
+  printer (arg, " PPPoE hdr: ver=0x%01x type=0x%01x code=0x%02x "
+          "sid=0x%04x length=0x%04x ", (unsigned) p->hdr->ver,
+          (unsigned) p->hdr->type, (unsigned) p->hdr->code, (unsigned) p->hdr->sid,
+          (unsigned) ntohs (p->hdr->length));
+
+  switch (p->hdr->code) {
+  case PADI_CODE:
+    printer (arg, "(PADI)\n");
+    break;
+  case PADO_CODE:
+    printer (arg, "(PADO)\n");
+    break;
+  case PADR_CODE:
+    printer (arg, "(PADR)\n");
+    break;
+  case PADS_CODE:
+    printer (arg, "(PADS)\n");
+    break;
+  case PADT_CODE:
+    printer (arg, "(PADT)\n");
+    break;
+  default:
+    printer (arg, "(Unknown)\n");
+  }
+
+#if 0
+  if (ntohs (p->addr.sll_protocol) != ETH_P_PPPOE_DISC) {
+      len = ntohs (p->length);
+
+    if (len > 64)
+      printer (arg, " %.64B ...", (p + 1));
+    else
+      printer (arg, " %.*B", len, p + 1);
+
+    return;
+  }
+#endif
+
+  for(t = (struct pppoe_tag *) (&p->hdr->tag);
+      (t < (struct pppoe_tag *) ((char *) (&p->hdr->tag) + ntohs (p->hdr->length))) &&
+         ntohs (t->tag_type) != PTT_EOL;
+      t = (struct pppoe_tag *) ((char *) (t + 1) + ntohs (t->tag_len))) {
+      format_tag (t, printer, arg);
+  }
+}
+
+/*
+ * format_tag - make a readable representation of a tag,
+ * calling `printer(arg, format, ...)' to output it.
+ */
+static void
+format_tag (struct pppoe_tag *t,
+              void (*printer) (void *, char *,...),
+              void *arg)
+{
+    printer (arg, " PPPoE tag: type=%04x length=%04x ",
+            ntohs (t->tag_type), ntohs (t->tag_len));
+    switch ( t->tag_type ) {
+    case PTT_EOL:
+      printer (arg, "(End of list)");
+      break;
+    case PTT_SRV_NAME:
+      printer (arg, "(Service name)");
+      break;
+    case PTT_AC_NAME:
+      printer (arg, "(AC Name)");
+      break;
+    case PTT_HOST_UNIQ:
+      printer (arg, "(Host Uniq)");
+      break;
+    case PTT_AC_COOKIE:
+      printer (arg, "(AC Cookie)");
+      break;
+    case PTT_VENDOR:
+      printer (arg, "(Vendor Specific)");
+      break;
+    case PTT_RELAY_SID:
+      printer (arg, "(Relay Session ID)");
+      break;
+    case PTT_SRV_ERR:
+      printer (arg, "(Service Name Error)");
+      break;
+    case PTT_SYS_ERR:
+      printer (arg, "(AC System Error)");
+      break;
+    case PTT_GEN_ERR:
+      printer (arg, "(Generic Error)");
+      break;
+    default:
+      printer (arg, "(Unknown)");
+    }
+    if (ntohs (t->tag_len) > 0)
+      switch ( t->tag_type ) {
+      case PTT_SRV_NAME:
+      case PTT_AC_NAME:
+      case PTT_SRV_ERR:
+      case PTT_SYS_ERR:
+      case PTT_GEN_ERR:        /* ascii data */
+       {
+         char *buf;
+         buf = malloc (ntohs (t->tag_len) + 1);
+         memset (buf, 0, ntohs (t->tag_len) + 1);
+         strncpy (buf, (char *) (t + 1), ntohs (t->tag_len));
+//       buf[ntohs (t->tag_len)] = '\0';
+         printer (arg, " data (UTF-8): %s", buf);
+         free (buf);
+         break;
+       }
+
+      case PTT_HOST_UNIQ:
+      case PTT_AC_COOKIE:
+      case PTT_RELAY_SID:
+       printer (arg, " data (bin): %.*B", ntohs (t->tag_len), (char *) (t + 1));
+       break;
+
+      default:
+       printer (arg, " unrecognized data");
+      }
+}
+
+/*
+ * poe_logit - does the hard work for poe_fatal et al.
+ */
+static void
+poe_logit (struct session *ses,int level, char *fmt, va_list args)
+{
+  int n;
+  char buf[256];
+
+  n = vpoe_slprintf (buf, sizeof (buf), fmt, args);
+  syslog (level, "%s", buf);
+  if (log_to_fd >= 0 && (level != LOG_DEBUG || ses->opt_debug)) {
+    if (buf[n - 1] != '\n')
+      buf[n++] = '\n';
+    if (write (log_to_fd, buf, n) != n)
+      log_to_fd = -1;
+  }
+}
+
+/*
+ * poe_fatal - log an poe_error message and poe_die horribly.
+ */
+void
+poe_fatal (struct session *ses, char *fmt,...)
+{
+  va_list pvar;
+
+  va_start (pvar, fmt);
+
+  poe_logit (ses,LOG_ERR, fmt, pvar);
+  va_end (pvar);
+
+  poe_die(1);                  /* as promised */
+}
+
+/*
+ * poe_error - log an poe_error message.
+ */
+void
+poe_error (struct session *ses,char *fmt,...)
+{
+  va_list pvar;
+
+  va_start (pvar, fmt);
+
+  poe_logit (ses,LOG_ERR, fmt, pvar);
+  va_end (pvar);
+}
+
+/*
+ * poe_warn - log a poe_warning message.
+ */
+void
+poe_warn (struct session *ses,char *fmt,...)
+{
+  va_list pvar;
+
+  va_start (pvar, fmt);
+
+  poe_logit (ses,LOG_WARNING, fmt, pvar);
+  va_end (pvar);
+}
+
+#if 0
+/*
+ * poe_notice - log a poe_notice-level message.
+ */
+void
+poe_notice (int log_to_fd ,char *fmt,...)
+{
+  va_list pvar;
+
+  va_start (pvar, fmt);
+
+  poe_logit (log_to_fd,LOG_NOTICE, fmt, pvar);
+  va_end (pvar);
+}
+
+#endif
+/*
+ * poe_info - log an poe_informational message.
+ */
+void
+poe_info (struct session *ses,char *fmt,...)
+{
+  va_list pvar;
+
+  va_start (pvar, fmt);
+
+  poe_logit (ses,LOG_INFO, fmt, pvar);
+  va_end (pvar);
+}
+
+/*
+ * poe_dbglog - log a debug message.
+ */
+void
+poe_dbglog (struct session *ses ,char *fmt,...)
+{
+  va_list pvar;
+
+  va_start (pvar, fmt);
+
+  poe_logit (ses,LOG_DEBUG, fmt, pvar);
+  va_end (pvar);
+}
+
+/*
+ * Create a file containing our process ID.
+ */
+void
+poe_create_pidfile (struct session *ses)
+{
+  FILE *pidfile;
+
+  sprintf (pidfilename, "%s%s.pid", _PATH_VARRUN, "pppoed");
+  if ((pidfile = fopen (pidfilename, "w")) != NULL) {
+    fprintf (pidfile, "%d\n", getpid ());
+    (void) fclose (pidfile);
+  }
+  else {
+    poe_error (ses,"Failed to create pid file %s: %m", pidfilename);
+    pidfilename[0] = 0;
+  }
+}
+
+/*
+ * detach - detach us from the controlling terminal.
+ */
+void
+poe_detach (struct session *ses)
+{
+  if (ses->detached)
+    return;
+
+  if ((daemon (0, 0)) < 0) {
+    poe_error (ses,"Couldn't detach (daemon failed: %m)");
+#if 0
+    poe_die (1);                       /* or just return? */
+#endif
+  }
+  ses->detached = 1;
+  ses->log_to_fd = -1;
+  /* update pid files if they have been written already */
+  if (pidfilename[0])
+    poe_create_pidfile (ses);
+}
+
+/*
+ * cleanup - restore anything which needs to be restored before we exit
+ */
+/* ARGSUSED */
+static void
+cleanup ()
+{
+  if (pidfilename[0] != 0 && unlink (pidfilename) < 0 && errno != ENOENT)
+    syslog (LOG_INFO,"unable to delete pid file ");
+  pidfilename[0] = 0;
+}
+
+/*
+ * poe_die - clean up state and exit with the specified status.
+ */
+void
+poe_die (int status)
+{
+  cleanup ();
+  syslog (LOG_INFO, "Exit.");
+  exit (status);
+}