From: Paul Mackerras Date: Thu, 26 Jul 2001 20:03:32 +0000 (+0000) Subject: pppoe plugin stuff - Michal Ostrowski's version, lightly hacked X-Git-Tag: ppp-2.4.7~475 X-Git-Url: https://git.ozlabs.org/?p=ppp.git;a=commitdiff_plain;h=cdbb124cea5feeb4468ad3f2e5b5d17de583da6d;hp=e59b06f904eb25935f8223c80bb1f07dab26933e;ds=sidebyside pppoe plugin stuff - Michal Ostrowski's version, lightly hacked --- diff --git a/pppd/plugins/pppoe/Makefile.linux b/pppd/plugins/pppoe/Makefile.linux new file mode 100644 index 0000000..7fc97eb --- /dev/null +++ b/pppd/plugins/pppoe/Makefile.linux @@ -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 index 0000000..c0b5e5b --- /dev/null +++ b/pppd/plugins/pppoe/libpppoe.c @@ -0,0 +1,631 @@ +/* PPPoE support library "libpppoe" + * + * Copyright 2000 Michal Ostrowski , + * Jamal Hadi Salim + * + * 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(;itags[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 index 0000000..86a2ca7 --- /dev/null +++ b/pppd/plugins/pppoe/pppd_utils.c @@ -0,0 +1,162 @@ +/* PPPoE support library "libpppoe" + * + * Copyright 2000 Michal Ostrowski , + * Jamal Hadi Salim + * + * 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 \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 index 0000000..773e476 --- /dev/null +++ b/pppd/plugins/pppoe/pppoe.c @@ -0,0 +1,369 @@ +/* pppoe.c - pppd plugin to implement PPPoE protocol. + * + * Copyright 2000 Michal Ostrowski , + * Jamal Hadi Salim + * 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 +#include +#include +#include +#include +#include +#include +#include "pppoe.h" +#if _linux_ +extern int new_style_driver; /* From sys-linux.c */ +#include +#include +#include +#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 index 0000000..4490cc9 --- /dev/null +++ b/pppd/plugins/pppoe/pppoe.h @@ -0,0 +1,273 @@ +/* PPPoE support library "libpppoe" + * + * Copyright 2000 Michal Ostrowski , + * Jamal Hadi Salim + * + * 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 */ +#include /* strtoul(), realloc() */ +#include /* STDIN_FILENO,exec */ +#include /* memcpy() */ +#include /* errno */ +#include +#include +#include +#include +#include + +#include /* socket types */ +#include +#include +#include +#include +#include /* ioctl() */ +#include +#include /* socket() */ +#include /* ifreq struct */ +#include +#include + +#if __GLIBC__ >= 2 && __GLIBC_MINOR >= 1 +#include +//#include +#else +#include +#include +#include +#endif + + +#include + +/* + jamal: we really have to change this + to make it compatible to the 2.2 and + 2.3 kernel +*/ + +#include + + +#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 index 0000000..6f12f26 --- /dev/null +++ b/pppd/plugins/pppoe/pppoe_client.c @@ -0,0 +1,232 @@ +/* PPPoE support library "libpppoe" + * + * Copyright 2000 Michal Ostrowski , + * Jamal Hadi Salim + * + * 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 index 0000000..cddf257 --- /dev/null +++ b/pppd/plugins/pppoe/pppoe_relay.c @@ -0,0 +1,260 @@ +/* PPPoE support library "libpppoe" + * + * Copyright 2000 Michal Ostrowski , + * Jamal Hadi Salim + * + * 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 index 0000000..ff7a5ae --- /dev/null +++ b/pppd/plugins/pppoe/pppoe_server.c @@ -0,0 +1,143 @@ +/* PPPoE support library "libpppoe" + * + * Copyright 2000 Michal Ostrowski , + * Jamal Hadi Salim + * + * 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 + +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 index 0000000..98079be --- /dev/null +++ b/pppd/plugins/pppoe/pppoed.c @@ -0,0 +1,283 @@ +/* PPPoE support library "libpppoe" + * + * Copyright 2000 Jamal Hadi Salim + * + * 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 : overrides the default interface of eth0"); + fprintf (stdout,"\n -S : starts pppoed in server mode"); + fprintf (stdout,"\n -R : 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 : specifies additional ppp options file"); + fprintf (stdout,"\n -C : ppp options file in /etc/ppp/peers/"); + fprintf (stdout,"\n -d : sets debug level"); + fprintf (stdout,"\n -D : prevents pppoed from detaching itself and running in the background"); + fprintf (stdout,"\n -P : selects a different pppd. Defaults to " _PATH_PPPD); + fprintf (stdout,"\n -A to select a specific AC by name"); + fprintf (stdout,"\n -E 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 index 0000000..7178766 --- /dev/null +++ b/pppd/plugins/pppoe/pppoefwd.c @@ -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 index 0000000..12ac069 --- /dev/null +++ b/pppd/plugins/pppoe/pppoehash.c @@ -0,0 +1,91 @@ +/* PPPoE support library "libpppoe" + * + * Copyright 2000 Michal Ostrowski , + * Jamal Hadi Salim + * + * 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 index 0000000..e6751c9 --- /dev/null +++ b/pppd/plugins/pppoe/utils.c @@ -0,0 +1,667 @@ + +/* + * utils.c - various utility functions used in pppoed. + * + * mostly stolen from ppp-2.3.10 by Marc Boucher + * + * 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 */ +#include /* strtoul(), realloc() */ +#include /* memcpy() */ +#include /* STDIN_FILENO,exec */ +#include /* errno */ + +#include + +#include +#include + +#include +#include +#include +#include +#include + +#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); +}