diff options
author | Kozlov Dmitry <dima@server> | 2010-10-08 12:16:07 +0400 |
---|---|---|
committer | Kozlov Dmitry <dima@server> | 2010-10-08 12:16:07 +0400 |
commit | c258ec9bc665fdbd479498a77aea5589b316e074 (patch) | |
tree | 4db52663a053e7237e06d1474e603f97b17aafa2 | |
parent | 66ecf5cd90f437b508749e64914daed0a9e2ba3e (diff) | |
download | accel-ppp-c258ec9bc665fdbd479498a77aea5589b316e074.tar.gz accel-ppp-c258ec9bc665fdbd479498a77aea5589b316e074.zip |
ctrl: implemented PPPoE server
-rw-r--r-- | accel-pptpd/ctrl/CMakeLists.txt | 1 | ||||
-rw-r--r-- | accel-pptpd/ctrl/pppoe.c | 884 | ||||
-rw-r--r-- | accel-pptpd/ctrl/pppoe.h | 59 | ||||
-rw-r--r-- | accel-pptpd/include/events.h | 2 | ||||
-rw-r--r-- | accel-pptpd/include/if_pppox.h | 13 | ||||
-rw-r--r-- | accel-pptpd/ppp/ppp.c | 2 | ||||
-rw-r--r-- | accel-pptpd/ppp/ppp_ccp.c | 2 | ||||
-rw-r--r-- | accel-pptpd/ppp/ppp_fsm.c | 6 | ||||
-rw-r--r-- | accel-pptpd/ppp/ppp_ipcp.c | 2 | ||||
-rw-r--r-- | rfc/rfc2516.txt | 955 |
10 files changed, 1920 insertions, 6 deletions
diff --git a/accel-pptpd/ctrl/CMakeLists.txt b/accel-pptpd/ctrl/CMakeLists.txt index 53b0dc1..c26bdd5 100644 --- a/accel-pptpd/ctrl/CMakeLists.txt +++ b/accel-pptpd/ctrl/CMakeLists.txt @@ -1,6 +1,7 @@ INCLUDE_DIRECTORIES(${CMAKE_CURRENT_SOURCE_DIR}) ADD_LIBRARY(pptp SHARED pptp.c) +ADD_LIBRARY(pppoe SHARED pppoe.c) INSTALL(TARGETS pptp LIBRARY DESTINATION usr/lib/accel-pptp diff --git a/accel-pptpd/ctrl/pppoe.c b/accel-pptpd/ctrl/pppoe.c new file mode 100644 index 0000000..7a9616a --- /dev/null +++ b/accel-pptpd/ctrl/pppoe.c @@ -0,0 +1,884 @@ +#include <unistd.h> +#include <stdlib.h> +#include <stdio.h> +#include <stdarg.h> +#include <errno.h> +#include <string.h> +#include <pthread.h> +#include <fcntl.h> +#include <sys/socket.h> +#include <sys/ioctl.h> +#include <net/ethernet.h> +#include <netpacket/packet.h> +#include <arpa/inet.h> + +#include <openssl/md5.h> + +#include "events.h" +#include "triton.h" +#include "log.h" +#include "ppp.h" +#include "mempool.h" + +#include "pppoe.h" + +#include "memdebug.h" + + +struct pppoe_serv_t +{ + struct triton_context_t ctx; + struct triton_md_handler_t hnd; + uint8_t hwaddr[ETH_ALEN]; + const char *ifname; + + pthread_mutex_t lock; + struct pppoe_conn_t *conn[MAX_SID]; + uint16_t sid; +}; + +struct pppoe_conn_t +{ + struct triton_context_t ctx; + struct pppoe_serv_t *serv; + int disc_sock; + uint16_t sid; + uint8_t addr[ETH_ALEN]; + int ppp_started:1; + + struct pppoe_tag *relay_sid; + struct pppoe_tag *host_uniq; + + struct ppp_ctrl_t ctrl; + struct ppp_t ppp; +}; + +static int conf_verbose = 0; +static const char *conf_service_name = ""; +static const char *conf_ac_name = "accel-pptp"; + +static mempool_t conn_pool; + +#define SECRET_SIZE 16 +static uint8_t *secret; + +static uint8_t bc_addr[ETH_ALEN] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff}; + +static void pppoe_send_PADT(struct pppoe_conn_t *conn); + +static void disconnect(struct pppoe_conn_t *conn) +{ + if (conn->ppp_started) { + conn->ppp_started = 0; + ppp_terminate(&conn->ppp, 1); + } + + pppoe_send_PADT(conn); + + close(conn->disc_sock); + + triton_event_fire(EV_CTRL_FINISHED, &conn->ppp); + + if (conf_verbose) + log_ppp_info("disconnected\n"); + + pthread_mutex_lock(&conn->serv->lock); + conn->serv->conn[conn->sid] = NULL; + pthread_mutex_unlock(&conn->serv->lock); + + _free(conn->ctrl.calling_station_id); + _free(conn->ctrl.called_station_id); + if (conn->host_uniq) + _free(conn->host_uniq); + if (conn->relay_sid) + _free(conn->relay_sid); + + triton_context_unregister(&conn->ctx); + + mempool_free(conn); +} + +static void ppp_started(struct ppp_t *ppp) +{ + log_ppp_debug("pppoe: ppp started\n"); +} + +static void ppp_finished(struct ppp_t *ppp) +{ + struct pppoe_conn_t *conn = container_of(ppp, typeof(*conn), ppp); + + log_ppp_debug("pppoe: ppp finished\n"); + + if (conn->ppp_started) { + conn->ppp_started = 0; + disconnect(conn); + } +} + +static void pppoe_conn_close(struct triton_context_t *ctx) +{ + struct pppoe_conn_t *conn = container_of(ctx, typeof(*conn), ctx); + + if (conn->ppp_started) + ppp_terminate(&conn->ppp, 0); + else + disconnect(conn); +} + +static struct pppoe_conn_t *allocate_channel(struct pppoe_serv_t *serv, const uint8_t *addr, const struct pppoe_tag *host_uniq, const struct pppoe_tag *relay_sid) +{ + struct pppoe_conn_t *conn; + int sid; + + conn = mempool_alloc(conn_pool); + if (!conn) { + log_emerg("pppoe: out of memory\n"); + return NULL; + } + + memset(conn, 0, sizeof(*conn)); + + pthread_mutex_lock(&serv->lock); + for (sid = serv->sid + 1; sid != serv->sid; sid++) { + if (sid == MAX_SID) + sid = 1; + if (!serv->conn[sid]) { + conn->sid = sid; + serv->sid = sid; + serv->conn[sid] = conn; + break; + } + } + pthread_mutex_unlock(&serv->lock); + + if (!conn->sid) { + log_warn("pppoe: no free sid available\n"); + mempool_free(conn); + return NULL; + } + + conn->serv = serv; + memcpy(conn->addr, addr, ETH_ALEN); + + if (host_uniq) { + conn->host_uniq = _malloc(sizeof(*host_uniq) + ntohs(host_uniq->tag_len)); + memcpy(conn->host_uniq, host_uniq, sizeof(*host_uniq) + ntohs(host_uniq->tag_len)); + } + + if (relay_sid) { + conn->relay_sid = _malloc(sizeof(*relay_sid) + ntohs(relay_sid->tag_len)); + memcpy(conn->relay_sid, relay_sid, sizeof(*relay_sid) + ntohs(relay_sid->tag_len)); + } + + conn->ctx.close = pppoe_conn_close; + conn->ctrl.ctx = &conn->ctx; + conn->ctrl.started = ppp_started; + conn->ctrl.finished = ppp_finished; + conn->ctrl.max_mtu = MAX_PPPOE_MTU; + conn->ctrl.name = "pppoe"; + + conn->ctrl.calling_station_id = _malloc(19); + conn->ctrl.called_station_id = _malloc(19); + sprintf(conn->ctrl.calling_station_id, "%02x:%02x:%02x:%02x:%02x:%02x", + addr[0], addr[1], addr[2], addr[3], addr[4], addr[5]); + sprintf(conn->ctrl.called_station_id, "%02x:%02x:%02x:%02x:%02x:%02x", + serv->hwaddr[0], serv->hwaddr[1], serv->hwaddr[2], serv->hwaddr[3], serv->hwaddr[4], serv->hwaddr[5]); + + ppp_init(&conn->ppp); + + conn->ppp.ctrl = &conn->ctrl; + conn->ppp.chan_name = conn->ctrl.calling_station_id; + + triton_context_register(&conn->ctx, &conn->ppp); + triton_context_wakeup(&conn->ctx); + + triton_event_fire(EV_CTRL_STARTING, &conn->ppp); + triton_event_fire(EV_CTRL_STARTED, &conn->ppp); + + conn->disc_sock = dup(serv->hnd.fd); + + return conn; +} + +static int connect_channel(struct pppoe_conn_t *conn) +{ + int sock; + struct sockaddr_pppox sp; + + sock = socket(AF_PPPOX, SOCK_STREAM, PX_PROTO_OE); + if (!sock) { + log_error("pppoe: socket(PPPOX): %s\n", strerror(errno)); + return -1; + } + + memset(&sp, 0, sizeof(sp)); + + sp.sa_family = AF_PPPOX; + sp.sa_protocol = PX_PROTO_OE; + sp.sa_addr.pppoe.sid = htons(conn->sid); + strcpy(sp.sa_addr.pppoe.dev, conn->serv->ifname); + memcpy(sp.sa_addr.pppoe.remote, conn->addr, ETH_ALEN); + + if (connect(sock, (struct sockaddr *)&sp, sizeof(sp))) { + log_error("pppoe: connect: %s\n", strerror(errno)); + close(sock); + return -1; + } + + conn->ppp.fd = sock; + + if (establish_ppp(&conn->ppp)) { + close(sock); + return -1; + } + + return 0; +} + +static void print_tag_string(struct pppoe_tag *tag) +{ + int i; + + for (i = 0; i < ntohs(tag->tag_len); i++) + log_info("%c", tag->tag_data[i]); +} + +static void print_tag_octets(struct pppoe_tag *tag) +{ + int i; + + for (i = 0; i < ntohs(tag->tag_len); i++) + log_info("%02x", (uint8_t)tag->tag_data[i]); +} + +static void print_packet(uint8_t *pack) +{ + struct ethhdr *ethhdr = (struct ethhdr *)pack; + struct pppoe_hdr *hdr = (struct pppoe_hdr *)(pack + ETH_HLEN); + struct pppoe_tag *tag; + int n; + + log_info("[PPPoE "); + + switch (hdr->code) { + case CODE_PADI: + log_info("PADI"); + break; + case CODE_PADO: + log_info("PADO"); + break; + case CODE_PADR: + log_info("PADR"); + break; + case CODE_PADS: + log_info("PADS"); + break; + case CODE_PADT: + log_info("PADT"); + break; + } + + log_info(" %02x:%02x:%02x:%02x:%02x:%02x => %02x:%02x:%02x:%02x:%02x:%02x", + ethhdr->h_source[0], ethhdr->h_source[1], ethhdr->h_source[2], ethhdr->h_source[3], ethhdr->h_source[4], ethhdr->h_source[5], + ethhdr->h_dest[0], ethhdr->h_dest[1], ethhdr->h_dest[2], ethhdr->h_dest[3], ethhdr->h_dest[4], ethhdr->h_dest[5]); + + log_info(" sid=%04x", ntohs(hdr->sid)); + + for (n = 0; n < ntohs(hdr->length); n += sizeof(*tag) + ntohs(tag->tag_len)) { + tag = (struct pppoe_tag *)(pack + ETH_HLEN + sizeof(*hdr) + n); + switch (ntohs(tag->tag_type)) { + case TAG_END_OF_LIST: + log_info(" <End-Of-List>"); + break; + case TAG_SERVICE_NAME: + log_info(" <Service-Name "); + print_tag_string(tag); + log_info(">"); + break; + case TAG_AC_NAME: + log_info(" <AC-Name "); + print_tag_string(tag); + log_info(">"); + break; + case TAG_HOST_UNIQ: + log_info(" <Host-Uniq "); + print_tag_octets(tag); + log_info(">"); + break; + case TAG_AC_COOKIE: + log_info(" <AC-Cookie "); + print_tag_octets(tag); + log_info(">"); + break; + case TAG_VENDOR_SPECIFIC: + log_info(" <Vendor-Specific>"); + break; + case TAG_RELAY_SESSION_ID: + log_info(" <Relay-Session-Id"); + print_tag_octets(tag); + log_info(">"); + break; + case TAG_SERVICE_NAME_ERROR: + log_info(" <Service-Name-Error>"); + break; + case TAG_AC_SYSTEM_ERROR: + log_info(" <AC-System-Error>"); + break; + case TAG_GENERIC_ERROR: + log_info(" <Generic-Error>"); + break; + default: + log_info(" <Unknown (%x)>", ntohs(tag->tag_type)); + break; + } + } + + log_info("]\n"); +} + +static void generate_cookie(const uint8_t *src, const uint8_t *dst, const struct pppoe_tag *host_uniq, uint8_t *cookie) +{ + MD5_CTX ctx; + + MD5_Init(&ctx); + MD5_Update(&ctx, secret, SECRET_SIZE); + MD5_Update(&ctx, src, ETH_ALEN); + MD5_Update(&ctx, dst, ETH_ALEN); + if (host_uniq) + MD5_Update(&ctx, host_uniq->tag_data, ntohs(host_uniq->tag_len)); + MD5_Update(&ctx, conf_ac_name, strlen(conf_ac_name)); + MD5_Update(&ctx, secret, SECRET_SIZE); + MD5_Final(cookie, &ctx); +} + +static void setup_header(uint8_t *pack, const uint8_t *src, const uint8_t *dst, int code, uint16_t sid) +{ + struct ethhdr *ethhdr = (struct ethhdr *)pack; + struct pppoe_hdr *hdr = (struct pppoe_hdr *)(pack + ETH_HLEN); + + memcpy(ethhdr->h_source, src, ETH_ALEN); + memcpy(ethhdr->h_dest, dst, ETH_ALEN); + ethhdr->h_proto = htons(ETH_P_PPP_DISC); + + hdr->ver = 1; + hdr->type = 1; + hdr->code = code; + hdr->sid = htons(sid); + hdr->length = 0; +} + +static void add_tag(uint8_t *pack, int type, const uint8_t *data, int len) +{ + struct pppoe_hdr *hdr = (struct pppoe_hdr *)(pack + ETH_HLEN); + struct pppoe_tag *tag = (struct pppoe_tag *)(pack + ETH_HLEN + sizeof(*hdr) + ntohs(hdr->length)); + + tag->tag_type = htons(type); + tag->tag_len = htons(len); + memcpy(tag->tag_data, data, len); + + hdr->length = htons(ntohs(hdr->length) + sizeof(*tag) + len); +} + +static void add_tag2(uint8_t *pack, const struct pppoe_tag *t) +{ + struct pppoe_hdr *hdr = (struct pppoe_hdr *)(pack + ETH_HLEN); + struct pppoe_tag *tag = (struct pppoe_tag *)(pack + ETH_HLEN + sizeof(*hdr) + ntohs(hdr->length)); + + memcpy(tag, t, sizeof(*t) + ntohs(t->tag_len)); + + hdr->length = htons(ntohs(hdr->length) + sizeof(*tag) + ntohs(t->tag_len)); +} + +static void pppoe_send(int fd, const uint8_t *pack) +{ + struct pppoe_hdr *hdr = (struct pppoe_hdr *)(pack + ETH_HLEN); + int n, s; + + s = ETH_HLEN + sizeof(*hdr) + ntohs(hdr->length); + n = write(fd, pack, s); + if (n < 0 ) + log_error("pppoe: write: %s\n", strerror(errno)); + else if (n != s) { + log_warn("pppoe: short write %i/%i\n", n,s); + } +} + +static void pppoe_send_PADO(struct pppoe_serv_t *serv, const uint8_t *addr, const struct pppoe_tag *host_uniq, const struct pppoe_tag *relay_sid) +{ + uint8_t pack[ETHER_MAX_LEN]; + uint8_t cookie[MD5_DIGEST_LENGTH]; + + setup_header(pack, serv->hwaddr, addr, CODE_PADO, 0); + + add_tag(pack, TAG_AC_NAME, (uint8_t *)conf_ac_name, strlen(conf_ac_name)); + add_tag(pack, TAG_SERVICE_NAME, (uint8_t *)conf_service_name, strlen(conf_service_name)); + + generate_cookie(serv->hwaddr, addr, host_uniq, cookie); + add_tag(pack, TAG_AC_COOKIE, cookie, MD5_DIGEST_LENGTH); + + if (host_uniq) + add_tag2(pack, host_uniq); + + if (relay_sid) + add_tag2(pack, relay_sid); + + if (conf_verbose) { + log_info("send "); + print_packet(pack); + } + + pppoe_send(serv->hnd.fd, pack); +} + +static void pppoe_send_err(struct pppoe_serv_t *serv, const uint8_t *addr, const struct pppoe_tag *host_uniq, const struct pppoe_tag *relay_sid, int code, int tag_type) +{ + uint8_t pack[ETHER_MAX_LEN]; + + setup_header(pack, serv->hwaddr, addr, code, 0); + + add_tag(pack, TAG_AC_NAME, (uint8_t *)conf_ac_name, strlen(conf_ac_name)); + add_tag(pack, tag_type, NULL, 0); + + if (host_uniq) + add_tag2(pack, host_uniq); + + if (relay_sid) + add_tag2(pack, relay_sid); + + if (conf_verbose) { + log_info("send "); + print_packet(pack); + } + + pppoe_send(serv->hnd.fd, pack); +} + +static void pppoe_send_PADS(struct pppoe_conn_t *conn) +{ + uint8_t pack[ETHER_MAX_LEN]; + + setup_header(pack, conn->serv->hwaddr, conn->addr, CODE_PADS, conn->sid); + + add_tag(pack, TAG_AC_NAME, (uint8_t *)conf_ac_name, strlen(conf_ac_name)); + add_tag(pack, TAG_SERVICE_NAME, (uint8_t *)conf_service_name, strlen(conf_service_name)); + + if (conn->host_uniq) + add_tag2(pack, conn->host_uniq); + + if (conn->relay_sid) + add_tag2(pack, conn->relay_sid); + + if (conf_verbose) { + log_info("send "); + print_packet(pack); + } + + pppoe_send(conn->disc_sock, pack); +} + +static void pppoe_send_PADT(struct pppoe_conn_t *conn) +{ + uint8_t pack[ETHER_MAX_LEN]; + + setup_header(pack, conn->serv->hwaddr, conn->addr, CODE_PADT, conn->sid); + + add_tag(pack, TAG_AC_NAME, (uint8_t *)conf_ac_name, strlen(conf_ac_name)); + add_tag(pack, TAG_SERVICE_NAME, (uint8_t *)conf_service_name, strlen(conf_service_name)); + + if (conn->host_uniq) + add_tag2(pack, conn->host_uniq); + + if (conn->relay_sid) + add_tag2(pack, conn->relay_sid); + + if (conf_verbose) { + log_info("send "); + print_packet(pack); + } + + pppoe_send(conn->disc_sock, pack); +} + +static void pppoe_recv_PADI(struct pppoe_serv_t *serv, uint8_t *pack, int size) +{ + struct ethhdr *ethhdr = (struct ethhdr *)pack; + struct pppoe_hdr *hdr = (struct pppoe_hdr *)(pack + ETH_HLEN); + struct pppoe_tag *tag; + struct pppoe_tag *host_uniq_tag = NULL; + struct pppoe_tag *relay_sid_tag = NULL; + int n, service_match = 0; + + if (hdr->sid) { + log_warn("pppoe: discaring PADI packet (sid is not zero)\n"); + return; + } + + if (conf_verbose) { + log_info("recv "); + print_packet(pack); + } + + for (n = 0; n < ntohs(hdr->length); n += sizeof(*tag) + ntohs(tag->tag_len)) { + tag = (struct pppoe_tag *)(pack + ETH_HLEN + sizeof(*hdr) + n); + switch (ntohs(tag->tag_type)) { + case TAG_END_OF_LIST: + break; + case TAG_SERVICE_NAME: + if (tag->tag_len == 0) + service_match = 1; + else if (conf_service_name) { + if (ntohs(tag->tag_len) != strlen(conf_service_name)) + break; + if (memcmp(tag->tag_data, conf_service_name, ntohs(tag->tag_len))) + break; + service_match = 1; + } + break; + case TAG_HOST_UNIQ: + host_uniq_tag = tag; + break; + case TAG_RELAY_SESSION_ID: + relay_sid_tag = tag; + break; + } + } + + if (!service_match) { + if (conf_verbose) + log_warn("pppoe: discarding PADI packet (Service-Name mismatch)\n"); + return; + } + + pppoe_send_PADO(serv, ethhdr->h_source, host_uniq_tag, relay_sid_tag); +} + +static void pppoe_recv_PADR(struct pppoe_serv_t *serv, uint8_t *pack, int size) +{ + struct ethhdr *ethhdr = (struct ethhdr *)pack; + struct pppoe_hdr *hdr = (struct pppoe_hdr *)(pack + ETH_HLEN); + struct pppoe_tag *tag; + struct pppoe_tag *host_uniq_tag = NULL; + struct pppoe_tag *relay_sid_tag = NULL; + struct pppoe_tag *ac_cookie_tag = NULL; + uint8_t cookie[MD5_DIGEST_LENGTH]; + int n, service_match = 0; + struct pppoe_conn_t *conn; + + if (!memcmp(ethhdr->h_dest, bc_addr, ETH_ALEN)) { + if (conf_verbose) + log_warn("pppoe: discard PADR (destination address is broadcast)\n"); + return; + } + + if (hdr->sid) { + if (conf_verbose) + log_warn("pppoe: discaring PADR packet (sid is not zero)\n"); + return; + } + + if (conf_verbose) { + log_info("recv "); + print_packet(pack); + } + + for (n = 0; n < ntohs(hdr->length); n += sizeof(*tag) + ntohs(tag->tag_len)) { + tag = (struct pppoe_tag *)(pack + ETH_HLEN + sizeof(*hdr) + n); + switch (ntohs(tag->tag_type)) { + case TAG_END_OF_LIST: + break; + case TAG_SERVICE_NAME: + if (tag->tag_len == 0) + service_match = 1; + else if (conf_service_name) { + if (ntohs(tag->tag_len) != strlen(conf_service_name)) + break; + if (memcmp(tag->tag_data, conf_service_name, ntohs(tag->tag_len))) + break; + service_match = 1; + } + break; + case TAG_HOST_UNIQ: + host_uniq_tag = tag; + break; + case TAG_AC_COOKIE: + ac_cookie_tag = tag; + break; + case TAG_RELAY_SESSION_ID: + relay_sid_tag = tag; + break; + } + } + + if (!ac_cookie_tag) { + if (conf_verbose) + log_warn("pppoe: discard PADR packet (no AC-Cookie tag present)\n"); + return; + } + + if (ntohs(ac_cookie_tag->tag_len) != MD5_DIGEST_LENGTH) { + if (conf_verbose) + log_warn("pppoe: discard PADR packet (incorrect AC-Cookie tag length)\n"); + return; + } + + generate_cookie(serv->hwaddr, ethhdr->h_source, host_uniq_tag, cookie); + + if (memcmp(cookie, ac_cookie_tag->tag_data, MD5_DIGEST_LENGTH)) { + if (conf_verbose) + log_warn("pppoe: discard PADR packet (incorrect AC-Cookie)\n"); + return; + } + + if (!service_match) { + if (conf_verbose) + log_warn("pppoe: Service-Name mismatch\n"); + pppoe_send_err(serv, ethhdr->h_source, host_uniq_tag, relay_sid_tag, CODE_PADS, TAG_SERVICE_NAME_ERROR); + return; + } + + conn = allocate_channel(serv, ethhdr->h_source, host_uniq_tag, relay_sid_tag); + if (!conn) + pppoe_send_err(serv, ethhdr->h_source, host_uniq_tag, relay_sid_tag, CODE_PADS, TAG_AC_SYSTEM_ERROR); + else { + pppoe_send_PADS(conn); + if (connect_channel(conn)) + disconnect(conn); + else + conn->ppp_started = 1; + } +} + +static void pppoe_recv_PADT(struct pppoe_serv_t *serv, uint8_t *pack) +{ + struct ethhdr *ethhdr = (struct ethhdr *)pack; + struct pppoe_hdr *hdr = (struct pppoe_hdr *)(pack + ETH_HLEN); + struct pppoe_conn_t *conn; + + if (!memcmp(ethhdr->h_dest, bc_addr, ETH_ALEN)) { + if (conf_verbose) + log_warn("pppoe: discard PADT (destination address is broadcast)\n"); + return; + } + + if (conf_verbose) { + log_info("recv "); + print_packet(pack); + } + + pthread_mutex_lock(&serv->lock); + conn = serv->conn[ntohs(hdr->sid)]; + if (conn && !memcmp(conn->addr, ethhdr->h_source, ETH_ALEN)) + triton_context_call(&conn->ctx, (void (*)(void *))disconnect, conn); + pthread_mutex_unlock(&serv->lock); +} + +static int pppoe_serv_read(struct triton_md_handler_t *h) +{ + struct pppoe_serv_t *serv = container_of(h, typeof(*serv), hnd); + uint8_t pack[ETHER_MAX_LEN]; + struct ethhdr *ethhdr = (struct ethhdr *)pack; + struct pppoe_hdr *hdr = (struct pppoe_hdr *)(pack + ETH_HLEN); + int n; + + n = read(h->fd, pack, sizeof(pack)); + if (n < 0) { + log_error("pppoe: read: %s\n", strerror(errno)); + return 0; + } + + if (n < ETH_HLEN + sizeof(*hdr)) { + if (conf_verbose) + log_warn("pppoe: short packet received (%i)\n", n); + return 0; + } + + if (memcmp(ethhdr->h_dest, bc_addr, ETH_ALEN) && memcmp(ethhdr->h_dest, serv->hwaddr, ETH_ALEN)) + return 0; + + if (!memcmp(ethhdr->h_source, bc_addr, ETH_ALEN)) { + if (conf_verbose) + log_warn("pppoe: discarding packet (host address is broadcast)\n"); + return 0; + } + + if ((ethhdr->h_source[0] & 1) != 0) { + if (conf_verbose) + log_warn("pppoe: discarding packet (host address is not unicast)\n"); + return 0; + } + + if (n < ETH_HLEN + sizeof(*hdr) + ntohs(hdr->length)) { + if (conf_verbose) + log_warn("pppoe: short packet received\n"); + return 0; + } + + if (hdr->ver != 1) { + if (conf_verbose) + log_warn("pppoe: discarding packet (unsupported version %i)\n", hdr->ver); + return 0; + } + + if (hdr->type != 1) { + if (conf_verbose) + log_warn("pppoe: discarding packet (unsupported type %i)\n", hdr->type); + } + + switch (hdr->code) { + case CODE_PADI: + pppoe_recv_PADI(serv, pack, n); + break; + case CODE_PADR: + pppoe_recv_PADR(serv, pack, n); + break; + case CODE_PADT: + pppoe_recv_PADT(serv, pack); + break; + } + + return 0; +} + +static void pppoe_serv_close(struct triton_context_t *ctx) +{ + struct pppoe_serv_t *serv = container_of(ctx, typeof(*serv), ctx); + + triton_md_unregister_handler(&serv->hnd); + close(serv->hnd.fd); + triton_context_unregister(&serv->ctx); +} + +static void pppoe_start_server(const char *ifname) +{ + struct pppoe_serv_t *serv = _malloc(sizeof(*serv)); + int sock; + int opt = 1; + struct ifreq ifr; + struct sockaddr_ll sa; + + memset(serv, 0, sizeof(*serv)); + + sock = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_PPP_DISC)); + if (sock < 0) { + log_emerg("pppoe: socket: %s\n", strerror(errno)); + _free(serv); + return; + } + + if (setsockopt(sock, SOL_SOCKET, SO_BROADCAST, &opt, sizeof(opt))) { + log_emerg("pppoe: setsockopt(SO_BROADCAST): %s\n", strerror(errno)); + goto out_err; + } + + strncpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name)); + if (ioctl(sock, SIOCGIFHWADDR, &ifr)) { + log_emerg("pppoe: ioctl(SIOCGIFHWADDR): %s\n", strerror(errno)); + goto out_err; + } + +#ifdef ARPHDR_ETHER + if (ifr.ifr_hwaddr.sa_family != ARPHDR_ETHER) { + log_emerg("pppoe: interface %s is not ethernet\n", ifname); + goto out_err; + } +#endif + + if ((ifr.ifr_hwaddr.sa_data[0] & 1) != 0) { + log_emerg("pppoe: interface %s has not unicast address\n", ifname); + goto out_err; + } + + memcpy(serv->hwaddr, ifr.ifr_hwaddr.sa_data, ETH_ALEN); + + if (ioctl(sock, SIOCGIFMTU, &ifr)) { + log_emerg("pppoe: ioctl(SIOCGIFMTU): %s\n", strerror(errno)); + goto out_err; + } + + if (ifr.ifr_mtu < ETH_DATA_LEN) + log_emerg("pppoe: interface %s has MTU of %i, should be %i\n", ifname, ifr.ifr_mtu, ETH_DATA_LEN); + + if (ioctl(sock, SIOCGIFINDEX, &ifr)) { + log_emerg("pppoe: ioctl(SIOCGIFINDEX): %s\n", strerror(errno)); + goto out_err; + } + + memset(&sa, 0, sizeof(sa)); + sa.sll_family = AF_PACKET; + sa.sll_protocol = htons(ETH_P_PPP_DISC); + sa.sll_ifindex = ifr.ifr_ifindex; + + if (bind(sock, (struct sockaddr *)&sa, sizeof(sa))) { + log_emerg("pppoe: bind: %s\n", strerror(errno)); + goto out_err; + } + + serv->ctx.close = pppoe_serv_close; + serv->hnd.fd = sock; + serv->hnd.read = pppoe_serv_read; + serv->ifname = ifname; + pthread_mutex_init(&serv->lock, NULL); + + triton_context_register(&serv->ctx, NULL); + triton_md_register_handler(&serv->ctx, &serv->hnd); + triton_md_enable_handler(&serv->hnd, MD_MODE_READ); + triton_context_wakeup(&serv->ctx); + + return; + +out_err: + close(sock); + _free(serv); +} + +static int init_secret(void) +{ + int fd; + + secret = malloc(SECRET_SIZE); + + fd = open("/dev/urandom", O_RDONLY); + if (fd < 0) { + log_emerg("pppoe: cann't open /dev/urandom: %s\n", strerror(errno)); + return -1; + } + + if (read(fd, secret, SECRET_SIZE) < 0) { + log_emerg("pppoe: faild to read /dev/urandom\n", strerror(errno)); + close(fd); + return -1; + } + + close(fd); + + return 0; +} + +static void __init pppoe_init(void) +{ + struct conf_sect_t *s = conf_get_section("pppoe"); + struct conf_option_t *opt; + + conn_pool = mempool_create(sizeof(struct pppoe_conn_t)); + + if (init_secret()) + _exit(EXIT_FAILURE); + + if (!s) { + log_emerg("pppoe: no configuration, disabled...\n"); + return; + } + + list_for_each_entry(opt, &s->items, entry) { + if (!strcmp(opt->name, "interface")) + pppoe_start_server(opt->val); + else if (!strcmp(opt->name, "verbose")) { + if (atoi(opt->val) > 0) + conf_verbose = 1; + } else if (!strcmp(opt->name, "ac-name") || !strcmp(opt->name, "AC-Name")) + conf_ac_name = opt->val; + else if (!strcmp(opt->name, "service-name") || !strcmp(opt->name, "Service-Name")) + conf_service_name = opt->val; + } +} + diff --git a/accel-pptpd/ctrl/pppoe.h b/accel-pptpd/ctrl/pppoe.h new file mode 100644 index 0000000..0506108 --- /dev/null +++ b/accel-pptpd/ctrl/pppoe.h @@ -0,0 +1,59 @@ +#ifndef __PPPOE_H +#define __PPPOE_H + +#include <linux/if.h> +#include <linux/if_pppox.h> + +/* PPPoE codes */ +#define CODE_PADI 0x09 +#define CODE_PADO 0x07 +#define CODE_PADR 0x19 +#define CODE_PADS 0x65 +#define CODE_PADT 0xA7 +#define CODE_SESS 0x00 + +/* PPPoE Tags */ +#define TAG_END_OF_LIST 0x0000 +#define TAG_SERVICE_NAME 0x0101 +#define TAG_AC_NAME 0x0102 +#define TAG_HOST_UNIQ 0x0103 +#define TAG_AC_COOKIE 0x0104 +#define TAG_VENDOR_SPECIFIC 0x0105 +#define TAG_RELAY_SESSION_ID 0x0110 +#define TAG_SERVICE_NAME_ERROR 0x0201 +#define TAG_AC_SYSTEM_ERROR 0x0202 +#define TAG_GENERIC_ERROR 0x0203 + +/* Discovery phase states */ +#define STATE_SENT_PADI 0 +#define STATE_RECEIVED_PADO 1 +#define STATE_SENT_PADR 2 +#define STATE_SESSION 3 +#define STATE_TERMINATED 4 + +/* Header size of a PPPoE packet */ +#define PPPOE_OVERHEAD 6 /* type, code, session, length */ +#define HDR_SIZE (sizeof(struct ethhdr) + PPPOE_OVERHEAD) +#define MAX_PPPOE_PAYLOAD (ETH_DATA_LEN - PPPOE_OVERHEAD) +#define MAX_PPPOE_MTU (MAX_PPPOE_PAYLOAD - 2) + +#define MAX_SID 65534 + +struct pppoe_tag_t +{ + struct list_head entry; + int type; + int len; +}; + +struct pppoe_packet_t +{ + uint8_t src[ETH_ALEN]; + uint8_t dst[ETH_ALEN]; + int code; + uint16_t sid; + struct list_head tags; +}; + +#endif + diff --git a/accel-pptpd/include/events.h b/accel-pptpd/include/events.h index 2f4c4e5..f0785be 100644 --- a/accel-pptpd/include/events.h +++ b/accel-pptpd/include/events.h @@ -1,6 +1,8 @@ #ifndef __EVENTS_H #define __EVENTS_H +#include <stdint.h> + #define EV_PPP_STARTING 1 #define EV_PPP_STARTED 2 #define EV_PPP_FINISHING 3 diff --git a/accel-pptpd/include/if_pppox.h b/accel-pptpd/include/if_pppox.h index da327a1..0532d18 100644 --- a/accel-pptpd/include/if_pppox.h +++ b/accel-pptpd/include/if_pppox.h @@ -20,11 +20,11 @@ #include <asm/types.h> #include <asm/byteorder.h> #include <linux/version.h> +#include <linux/if.h> +#include <linux/if_ether.h> #ifdef __KERNEL__ #include <linux/in.h> -#include <linux/if_ether.h> -#include <linux/if.h> #include <linux/netdevice.h> #include <linux/ppp_channel.h> #endif /* __KERNEL__ */ @@ -37,7 +37,13 @@ #define PF_PPPOX AF_PPPOX #endif /* !(AF_PPPOX) */ -struct pptp_addr{ +struct pppoe_addr { + __be16 sid; + unsigned char remote[ETH_ALEN]; + char dev[IFNAMSIZ]; +}; + +struct pptp_addr { __u16 call_id; struct in_addr sin_addr; }; @@ -57,6 +63,7 @@ struct sockaddr_pppox { sa_family_t sa_family; /* address family, AF_PPPOX */ unsigned int sa_protocol; /* protocol identifier */ union{ + struct pppoe_addr pppoe; struct pptp_addr pptp; }sa_addr; }__attribute__ ((packed)); diff --git a/accel-pptpd/ppp/ppp.c b/accel-pptpd/ppp/ppp.c index 3e51ecb..24795b1 100644 --- a/accel-pptpd/ppp/ppp.c +++ b/accel-pptpd/ppp/ppp.c @@ -80,7 +80,7 @@ int __export establish_ppp(struct ppp_t *ppp) { /* Open an instance of /dev/ppp and connect the channel to it */ if (ioctl(ppp->fd, PPPIOCGCHAN, &ppp->chan_idx) == -1) { - log_ppp_error("Couldn't get channel number\n"); + log_ppp_error("ioctl(PPPIOCGCHAN): %s\n", strerror(errno)); return -1; } diff --git a/accel-pptpd/ppp/ppp_ccp.c b/accel-pptpd/ppp/ppp_ccp.c index ae9e637..163881b 100644 --- a/accel-pptpd/ppp/ppp_ccp.c +++ b/accel-pptpd/ppp/ppp_ccp.c @@ -153,6 +153,8 @@ void ccp_layer_finish(struct ppp_layer_data_t *ld) ccp_set_flags(ccp->ppp->unit_fd, 0, 0); ccp->fsm.fsm_state = FSM_Closed; + + log_ppp_debug("ccp_layer_finished\n"); ppp_layer_finished(ccp->ppp, &ccp->ld); } diff --git a/accel-pptpd/ppp/ppp_fsm.c b/accel-pptpd/ppp/ppp_fsm.c index 4769dc8..3478d25 100644 --- a/accel-pptpd/ppp/ppp_fsm.c +++ b/accel-pptpd/ppp/ppp_fsm.c @@ -399,7 +399,7 @@ void ppp_fsm_recv_term_req(struct ppp_fsm_t *layer) layer->fsm_state=FSM_Req_Sent; break; default: - layer->send_term_req(layer); + layer->send_term_ack(layer); break; } } @@ -487,8 +487,10 @@ static void init_req_counter(struct ppp_fsm_t *layer,int timeout) } static void zero_req_counter(struct ppp_fsm_t *layer) { - layer->restart_timer.expire_tv.tv_sec=0; layer->restart_counter=0; + + if (!layer->restart_timer.tpd) + triton_timer_add(layer->ppp->ctrl->ctx, &layer->restart_timer, 0); } static void restart_timer_func(struct triton_timer_t *t) diff --git a/accel-pptpd/ppp/ppp_ipcp.c b/accel-pptpd/ppp/ppp_ipcp.c index c46b1bd..90e2b9c 100644 --- a/accel-pptpd/ppp/ppp_ipcp.c +++ b/accel-pptpd/ppp/ppp_ipcp.c @@ -116,6 +116,8 @@ void ipcp_layer_finish(struct ppp_layer_data_t *ld) log_ppp_debug("ipcp_layer_finish\n"); ipcp->fsm.fsm_state = FSM_Closed; + + log_ppp_debug("ipcp_layer_finished\n"); ppp_layer_finished(ipcp->ppp, &ipcp->ld); } diff --git a/rfc/rfc2516.txt b/rfc/rfc2516.txt new file mode 100644 index 0000000..5397c86 --- /dev/null +++ b/rfc/rfc2516.txt @@ -0,0 +1,955 @@ + + + + + + +Network Working Group L. Mamakos +Request for Comments: 2516 K. Lidl +Category: Informational J. Evarts + UUNET Technologies, Inc. + D. Carrel + D. Simone + RedBack Networks, Inc. + R. Wheeler + RouterWare, Inc. + February 1999 + + + A Method for Transmitting PPP Over Ethernet (PPPoE) + +Status of this Memo + + This memo provides information for the Internet community. It does + not specify an Internet standard of any kind. Distribution of this + memo is unlimited. + +Copyright Notice + + Copyright (C) The Internet Society (1999). All Rights Reserved. + +Abstract + + The Point-to-Point Protocol (PPP) [1] provides a standard method for + transporting multi-protocol datagrams over point-to-point links. + + This document describes how to build PPP sessions and encapsulate PPP + packets over Ethernet. + +Applicability + + This specification is intended to provide the facilities which are + defined for PPP, such as the Link Control Protocol, Network-layer + Control Protocols, authentication, and more. These capabilities + require a point-to-point relationship between the peers, and are not + designed for the multi-point relationships which are available in + Ethernet and other multi-access environments. + + This specification can be used by multiple hosts on a shared, + Ethernet to open PPP sessions to multiple destinations via one or + more bridging modems. It is intended to be used with broadband + remote access technologies that provide a bridged Ethernet topology, + when access providers wish to maintain the session abstraction + associated with PPP. + + + + +Mamakos, et. al. Informational [Page 1] + +RFC 2516 Transmitting PPP Over Ethernet February 1999 + + + This document describes the PPP Over Ethernet encapsulation that is + being deployed by RedBack Networks, RouterWare, UUNET and others. + +1. Introduction + + Modern access technologies are faced with several conflicting goals. + It is desirable to connect multiple hosts at a remote site through + the same customer premise access device. It is also a goal to + provide access control and billing functionality in a manner similar + to dial-up services using PPP. In many access technologies, the most + cost effective method to attach multiple hosts to the customer + premise access device, is via Ethernet. In addition, it is desirable + to keep the cost of this device as low as possible while requiring + little or no configuration. + + PPP over Ethernet (PPPoE) provides the ability to connect a network + of hosts over a simple bridging access device to a remote Access + Concentrator. With this model, each host utilizes it's own PPP stack + and the user is presented with a familiar user interface. Access + control, billing and type of service can be done on a per-user, + rather than a per-site, basis. + + To provide a point-to-point connection over Ethernet, each PPP + session must learn the Ethernet address of the remote peer, as well + as establish a unique session identifier. PPPoE includes a discovery + protocol that provides this. + +2. Conventions + + The keywords MUST, MUST NOT, REQUIRED, SHALL, SHALL NOT, SHOULD, + SHOULD NOT, RECOMMENDED, MAY, and OPTIONAL, when they appear in this + document, are to be interpreted as described in [2]. + +3. Protocol Overview + + PPPoE has two distinct stages. There is a Discovery stage and a PPP + Session stage. When a Host wishes to initiate a PPPoE session, it + must first perform Discovery to identify the Ethernet MAC address of + the peer and establish a PPPoE SESSION_ID. While PPP defines a + peer-to-peer relationship, Discovery is inherently a client-server + relationship. In the Discovery process, a Host (the client) + discovers an Access Concentrator (the server). Based on the network + topology, there may be more than one Access Concentrator that the + Host can communicate with. The Discovery stage allows the Host to + discover all Access Concentrators and then select one. When + Discovery completes successfully, both the Host and the selected + Access Concentrator have the information they will use to build their + point-to-point connection over Ethernet. + + + +Mamakos, et. al. Informational [Page 2] + +RFC 2516 Transmitting PPP Over Ethernet February 1999 + + + The Discovery stage remains stateless until a PPP session is + established. Once a PPP session is established, both the Host and + the Access Concentrator MUST allocate the resources for a PPP virtual + interface. + +4. Payloads + + The following packet formats are defined here. The payload contents + will be defined in the Discovery and PPP sections. + + An Ethernet frame is as follows: + + 1 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | DESTINATION_ADDR | + | (6 octets) | + | | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | SOURCE_ADDR | + | (6 octets) | + | | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | ETHER_TYPE (2 octets) | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + ~ ~ + ~ payload ~ + ~ ~ + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | CHECKSUM | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + + The DESTINATION_ADDR field contains either a unicast Ethernet + destination address, or the Ethernet broadcast address (0xffffffff). + For Discovery packets, the value is either a unicast or broadcast + address as defined in the Discovery section. For PPP session + traffic, this field MUST contain the peer's unicast address as + determined from the Discovery stage. + + The SOURCE_ADDR field MUST contains the Ethernet MAC address of the + source device. + + The ETHER_TYPE is set to either 0x8863 (Discovery Stage) or 0x8864 + (PPP Session Stage). + + + + + + + +Mamakos, et. al. Informational [Page 3] + +RFC 2516 Transmitting PPP Over Ethernet February 1999 + + + The Ethernet payload for PPPoE is as follows: + + 1 2 3 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | VER | TYPE | CODE | SESSION_ID | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | LENGTH | payload ~ + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + + The VER field is four bits and MUST be set to 0x1 for this version of + the PPPoE specification. + + The TYPE field is four bits and MUST be set to 0x1 for this version + of the PPPoE specification. + + The CODE field is eight bits and is defined below for the Discovery + and PPP Session stages. + + The SESSION_ID field is sixteen bits. It is an unsigned value in + network byte order. It's value is defined below for Discovery + packets. The value is fixed for a given PPP session and, in fact, + defines a PPP session along with the Ethernet SOURCE_ADDR and + DESTINATION_ADDR. A value of 0xffff is reserved for future use and + MUST NOT be used + + The LENGTH field is sixteen bits. The value, in network byte order, + indicates the length of the PPPoE payload. It does not include the + length of the Ethernet or PPPoE headers. + +5. Discovery Stage + + There are four steps to the Discovery stage. When it completes, both + peers know the PPPoE SESSION_ID and the peer's Ethernet address, + which together define the PPPoE session uniquely. The steps consist + of the Host broadcasting an Initiation packet, one or more Access + Concentrators sending Offer packets, the Host sending a unicast + Session Request packet and the selected Access Concentrator sending a + Confirmation packet. When the Host receives the Confirmation packet, + it may proceed to the PPP Session Stage. When the Access + Concentrator sends the Confirmation packet, it may proceed to the PPP + Session Stage. + + All Discovery Ethernet frames have the ETHER_TYPE field set to the + value 0x8863. + + + + + + +Mamakos, et. al. Informational [Page 4] + +RFC 2516 Transmitting PPP Over Ethernet February 1999 + + + The PPPoE payload contains zero or more TAGs. A TAG is a TLV (type- + length-value) construct and is defined as follows: + + 1 2 3 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | TAG_TYPE | TAG_LENGTH | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | TAG_VALUE ... ~ + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + + TAG_TYPE is a sixteen bit field in network byte order. Appendix A + contains a list of all TAG_TYPEs and their TAG_VALUEs. + + TAG_LENGTH is a sixteen bit field. It is an unsigned number in + network byte order, indicating the length in octets of the TAG_VALUE. + + If a discovery packet is received with a TAG of unknown TAG_TYPE, the + TAG MUST be ignored unless otherwise specified in this document. + This provides for backwards compatibility if/when new TAGs are added. + If new mandatory TAGs are added, the version number will be + incremented. + + Some example Discovery packets are shown in Appendix B. + +5.1 The PPPoE Active Discovery Initiation (PADI) packet + + The Host sends the PADI packet with the DESTINATION_ADDR set to the + broadcast address. The CODE field is set to 0x09 and the SESSION_ID + MUST be set to 0x0000. + + The PADI packet MUST contain exactly one TAG of TAG_TYPE Service- + Name, indicating the service the Host is requesting, and any number + of other TAG types. An entire PADI packet (including the PPPoE + header) MUST NOT exceed 1484 octets so as to leave sufficient room + for a relay agent to add a Relay-Session-Id TAG. + +5.2 The PPPoE Active Discovery Offer (PADO) packet + + When the Access Concentrator receives a PADI that it can serve, it + replies by sending a PADO packet. The DESTINATION_ADDR is the + unicast address of the Host that sent the PADI. The CODE field is + set to 0x07 and the SESSION_ID MUST be set to 0x0000. + + + + + + + + +Mamakos, et. al. Informational [Page 5] + +RFC 2516 Transmitting PPP Over Ethernet February 1999 + + + The PADO packet MUST contain one AC-Name TAG containing the Access + Concentrator's name, a Service-Name TAG identical to the one in the + PADI, and any number of other Service-Name TAGs indicating other + services that the Access Concentrator offers. If the Access + Concentrator can not serve the PADI it MUST NOT respond with a PADO. + +5.3 The PPPoE Active Discovery Request (PADR) packet + + Since the PADI was broadcast, the Host may receive more than one + PADO. The Host looks through the PADO packets it receives and + chooses one. The choice can be based on the AC-Name or the Services + offered. The Host then sends one PADR packet to the Access + Concentrator that it has chosen. The DESTINATION_ADDR field is set + to the unicast Ethernet address of the Access Concentrator that sent + the PADO. The CODE field is set to 0x19 and the SESSION_ID MUST be + set to 0x0000. + + The PADR packet MUST contain exactly one TAG of TAG_TYPE Service- + Name, indicating the service the Host is requesting, and any number + of other TAG types. + +5.4 The PPPoE Active Discovery Session-confirmation (PADS) packet + + When the Access Concentrator receives a PADR packet, it prepares to + begin a PPP session. It generates a unique SESSION_ID for the PPPoE + session and replies to the Host with a PADS packet. The + DESTINATION_ADDR field is the unicast Ethernet address of the Host + that sent the PADR. The CODE field is set to 0x65 and the SESSION_ID + MUST be set to the unique value generated for this PPPoE session. + + The PADS packet contains exactly one TAG of TAG_TYPE Service-Name, + indicating the service under which Access Concentrator has accepted + the PPPoE session, and any number of other TAG types. + + If the Access Concentrator does not like the Service-Name in the + PADR, then it MUST reply with a PADS containing a TAG of TAG_TYPE + Service-Name-Error (and any number of other TAG types). In this case + the SESSION_ID MUST be set to 0x0000. + +5.5 The PPPoE Active Discovery Terminate (PADT) packet + + This packet may be sent anytime after a session is established to + indicate that a PPPoE session has been terminated. It may be sent by + either the Host or the Access Concentrator. The DESTINATION_ADDR + field is a unicast Ethernet address, the CODE field is set to 0xa7 + and the SESSION_ID MUST be set to indicate which session is to be + terminated. No TAGs are required. + + + + +Mamakos, et. al. Informational [Page 6] + +RFC 2516 Transmitting PPP Over Ethernet February 1999 + + + When a PADT is received, no further PPP traffic is allowed to be sent + using that session. Even normal PPP termination packets MUST NOT be + sent after sending or receiving a PADT. A PPP peer SHOULD use the + PPP protocol itself to bring down a PPPoE session, but the PADT MAY + be used when PPP can not be used. + +6. PPP Session Stage + + Once the PPPoE session begins, PPP data is sent as in any other PPP + encapsulation. All Ethernet packets are unicast. The ETHER_TYPE + field is set to 0x8864. The PPPoE CODE MUST be set to 0x00. The + SESSION_ID MUST NOT change for that PPPoE session and MUST be the + value assigned in the Discovery stage. The PPPoE payload contains a + PPP frame. The frame begins with the PPP Protocol-ID. + + An example packet is shown in Appendix B. + +7. LCP Considerations + + The Magic Number LCP configuration option is RECOMMENDED, and the + Protocol Field Compression (PFC) option is NOT RECOMMENDED. An + implementation MUST NOT request any of the following options, and + MUST reject a request for such an option: + + Field Check Sequence (FCS) Alternatives, + + Address-and-Control-Field-Compression (ACFC), + + Asynchronous-Control-Character-Map (ACCM) + + The Maximum-Receive-Unit (MRU) option MUST NOT be negotiated to a + larger size than 1492. Since Ethernet has a maximum payload size of + 1500 octets, the PPPoE header is 6 octets and the PPP Protocol ID is + 2 octets, the PPP MTU MUST NOT be greater than 1492. + + It is RECOMMENDED that the Access Concentrator ocassionally send + Echo-Request packets to the Host to determine the state of the + session. Otherwise, if the Host terminates a session without sending + a Terminate-Request packet, the Access Concentrator will not be able + to determine that the session has gone away. + + When LCP terminates, the Host and Access concentrator MUST stop using + that PPPoE session. If the Host wishes to start another PPP session, + it MUST return to the PPPoE Discovery stage. + + + + + + + +Mamakos, et. al. Informational [Page 7] + +RFC 2516 Transmitting PPP Over Ethernet February 1999 + + +8. Other Considerations + + When a host does not receive a PADO packet within a specified amount + of time, it SHOULD resend it's PADI packet and double the waiting + period. This is repeated as many times as desired. If the Host is + waiting to receive a PADS packet, a similar timeout mechanism SHOULD + be used, with the Host re-sending the PADR. After a specified number + of retries, the Host SHOULD then resend a PADI packet. + + The ETHER_TYPEs used in this document (0x8863 and 0x8864) have been + assigned by the IEEE for use by PPP Over Ethernet (PPPoE). Use of + these values and the PPPoE VER (version) field uniquely identify this + protocol. + + UTF-8 [5] is used throughout this document instead of ASCII. UTF-8 + supports the entire ASCII character set while allowing for + international character sets as well. See [5] for more details. + +9. Security Considerations + + To help protect against Denial of Service (DOS) attacks, the Access + Concentrator can employ the AC-Cookie TAG. The Access Concentrator + SHOULD be able to uniquely regenerate the TAG_VALUE based on the PADR + SOURCE_ADDR. Using this, the Access Concentrator can ensure that the + PADI SOURCE_ADDR is indeed reachable and can then limit concurrent + sessions for that address. What algorithm to use is not defined and + left as an implementation detail. An example is HMAC [3] over the + Host MAC address using a key known only to the Access > Concentrator. + While the AC-Cookie is useful against some DOS attacks, it can not + protect against all DOS attacks and an Access Concentrator MAY employ + other means to protect resources. + + While the AC-Cookie is useful against some DOS attacks, it can not + protect against all DOS attacks and an Access Concentrator MAY employ + other means to protect resources. + + Many Access Concentrators will not wish to offer information + regarding what services they offer to an unauthenticated entity. In + that case the Access Concentrator should employ one of two policies. + It SHOULD never refuse a request based on the Service-Name TAG, and + always return the TAG_VALUE that was sent to it. Or it SHOULD only + accept requests with a Service-Name TAG with a zero TAG_LENGTH + (indicating any service). The former solution is RECOMMENDED. + +10. Acknowledgments + + This document is based on concepts discussed in several forums, + including the ADSL forum. + + + +Mamakos, et. al. Informational [Page 8] + +RFC 2516 Transmitting PPP Over Ethernet February 1999 + + + Copious amounts of text have been stolen from RFC 1661, RFC 1662 and + RFC 2364. + +11. References + + [1] Simpson, W., Editor, "The Point-to-Point Protocol (PPP)", STD 51, + RFC 1661, July 1994 + + [2] Bradner, S., "Key words for use in RFCs to Indicate Requirement + Levels", BCP 14, RFC 2119, March 1997. + + [3] Krawczyk, H., Bellare, M. and R. Canetti, "HMAC: Keyed-Hashing + for Message Authentication", RFC 2104, February 1998. + + [4] Reynolds, J. and J. Postel, "Assigned Numbers", STD 2, RFC 1700, + October 1994. See also: http://www.iana.org/numbers.html + + [5] Yergeau, F., "UTF-8, a transformation format of ISO 10646", RFC + 2279, January 1998. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +Mamakos, et. al. Informational [Page 9] + +RFC 2516 Transmitting PPP Over Ethernet February 1999 + + +Appendix A + + TAG_TYPES and TAG_VALUES + + 0x0000 End-Of-List + + This TAG indicates that there are no further TAGs in the list. The + TAG_LENGTH of this TAG MUST always be zero. Use of this TAG is + not required, but remains for backwards compatibility. + + 0x0101 Service-Name + + This TAG indicates that a service name follows. The TAG_VALUE is + an UTF-8 string that is NOT NULL terminated. When the TAG_LENGTH + is zero this TAG is used to indicate that any service is + acceptable. Examples of the use of the Service-Name TAG are to + indicate an ISP name or a class or quality of service. + + 0x0102 AC-Name + + This TAG indicates that a string follows which uniquely identifies + this particular Access Concentrator unit from all others. It may + be a combination of trademark, model, and serial id information, + or simply an UTF-8 rendition of the MAC address of the box. It is + not NULL terminated. + + 0x0103 Host-Uniq + + This TAG is used by a Host to uniquely associate an Access + Concentrator response (PADO or PADS) to a particular Host request + (PADI or PADR). The TAG_VALUE is binary data of any value and + length that the Host chooses. It is not interpreted by the Access + Concentrator. The Host MAY include a Host-Uniq TAG in a PADI or + PADR. If the Access Concentrator receives this TAG, it MUST + include the TAG unmodified in the associated PADO or PADS + response. + + 0x0104 AC-Cookie + + This TAG is used by the Access Concentrator to aid in protecting + against denial of service attacks (see the Security Considerations + section for an explanation of how this works). The Access + Concentrator MAY include this TAG in a PADO packet. If a Host + receives this TAG, it MUST return the TAG unmodified in the + following PADR. The TAG_VALUE is binary data of any value and + length and is not interpreted by the Host. + + + + + +Mamakos, et. al. Informational [Page 10] + +RFC 2516 Transmitting PPP Over Ethernet February 1999 + + + 0x0105 Vendor-Specific + + This TAG is used to pass vendor proprietary information. The + first four octets of the TAG_VALUE contain the vendor id and the + remainder is unspecified. The high-order octet of the vendor id + is 0 and the low-order 3 octets are the SMI Network Management + Private Enterprise Code of the Vendor in network byte order, as + defined in the Assigned Numbers RFC [4]. + + Use of this TAG is NOT RECOMMENDED. To ensure inter-operability, + an implementation MAY silently ignore a Vendor-Specific TAG. + + 0x0110 Relay-Session-Id + + This TAG MAY be added to any discovery packet by an intermediate + agent that is relaying traffic. The TAG_VALUE is opaque to both + the Host and the Access Concentrator. If either the Host or + Access Concentrator receives this TAG they MUST include it + unmodified in any discovery packet they send as a response. All + PADI packets MUST guarantee sufficient room for the addition of a + Relay-Session-Id TAG with a TAG_VALUE length of 12 octets. + + A Relay-Session-Id TAG MUST NOT be added if the discovery packet + already contains one. In that case the intermediate agent SHOULD + use the existing Relay-Session-Id TAG. If it can not use the + existing TAG or there is insufficient room to add a Relay- + Session-Id TAG, then it SHOULD return a Generic-Error TAG to the + sender. + + 0x0201 Service-Name-Error + + This TAG (typically with a zero-length data section) indicates + that for one reason or another, the requested Service-Name request + could not be honored. + + If there is data, and the first octet of the data is nonzero, then + it MUST be a printable UTF-8 string which explains why the request + was denied. This string MAY NOT be NULL terminated. + + 0x0202 AC-System-Error + + This TAG indicates that the Access Concentrator experienced some + error in performing the Host request. (For example insufficient + resources to create a virtual circuit.) It MAY be included in + PADS packets. + + + + + + +Mamakos, et. al. Informational [Page 11] + +RFC 2516 Transmitting PPP Over Ethernet February 1999 + + + If there is data, and the first octet of the data is nonzero, then + it MUST be a printable UTF-8 string which explains the nature of + the error. This string MAY NOT be NULL terminated. + + 0x0203 Generic-Error + + This TAG indicates an error. It can be added to PADO, PADR or + PADS packets when an unrecoverable error occurs and no other error + TAG is appropriate. If there is data then it MUST be an UTF-8 + string which explains the nature of the error. This string MUST + NOT be NULL terminated. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +Mamakos, et. al. Informational [Page 12] + +RFC 2516 Transmitting PPP Over Ethernet February 1999 + + +Appendix B + + The following are some example packets: + + A PADI packet: + + 1 2 3 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | 0xffffffff | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | 0xffff | Host_mac_addr | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Host_mac_addr (cont) | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | ETHER_TYPE = 0x8863 | v = 1 | t = 1 | CODE = 0x09 | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | SESSION_ID = 0x0000 | LENGTH = 0x0004 | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | TAG_TYPE = 0x0101 | TAG_LENGTH = 0x0000 | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +Mamakos, et. al. Informational [Page 13] + +RFC 2516 Transmitting PPP Over Ethernet February 1999 + + + A PADO packet: + + 1 2 3 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Host_mac_addr | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Host_mac_addr (cont) | Access_Concentrator_mac_addr | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Access_Concentrator_mac_addr (cont) | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | ETHER_TYPE = 0x8863 | v = 1 | t = 1 | CODE = 0x07 | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | SESSION_ID = 0x0000 | LENGTH = 0x0020 | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | TAG_TYPE = 0x0101 | TAG_LENGTH = 0x0000 | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | TAG_TYPE = 0x0102 | TAG_LENGTH = 0x0018 | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | 0x47 | 0x6f | 0x20 | 0x52 | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | 0x65 | 0x64 | 0x42 | 0x61 | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | 0x63 | 0x6b | 0x20 | 0x2d | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | 0x20 | 0x65 | 0x73 | 0x68 | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | 0x73 | 0x68 | 0x65 | 0x73 | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | 0x68 | 0x6f | 0x6f | 0x74 | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + + + + + + + + + + + + + + + + + + + + +Mamakos, et. al. Informational [Page 14] + +RFC 2516 Transmitting PPP Over Ethernet February 1999 + + + A PPP LCP packet: The PPP protocol value is shown (0xc021) but the + PPP payload is left to the reader. This is a packet from the Host to + the Access Concentrator. + + 1 2 3 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Access_Concentrator_mac_addr | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + |Access_Concentrator_mac_addr(c)| Host_mac_addr | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Host_mac_addr (cont) | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | ETHER_TYPE = 0x8864 | v = 1 | t = 1 | CODE = 0x00 | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | SESSION_ID = 0x1234 | LENGTH = 0x???? | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | PPP PROTOCOL = 0xc021 | PPP payload ~ + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + +Authors' Addresses + + Louis Mamakos + UUNET Technologies, Inc. + 3060 Williams Drive + Fairfax, VA 22031-4648 + United States of America + + EMail: louie@uu.net + + + Kurt Lidl + UUNET Technologies, Inc. + 3060 Williams Drive + Fairfax, VA 22031-4648 + United States of America + + EMail: lidl@uu.net + + + Jeff Evarts + UUNET Technologies, Inc. + 3060 Williams Drive + Fairfax, VA 22031-4648 + United States of America + + EMail: jde@uu.net + + + + +Mamakos, et. al. Informational [Page 15] + +RFC 2516 Transmitting PPP Over Ethernet February 1999 + + + David Carrel + RedBack Networks, Inc. + 1389 Moffett Park Drive + Sunnyvale, CA 94089-1134 + United States of America + + EMail: carrel@RedBack.net + + + Dan Simone + RedBack Networks, Inc. + 1389 Moffett Park Drive + Sunnyvale, CA 94089-1134 + United States of America + + EMail:dan@RedBack.net + + + Ross Wheeler + RouterWare, Inc. + 3961 MacArthur Blvd., Suite 212 + Newport Beach, CA 92660 + United States of America + + EMail: ross@routerware.com + + + + + + + + + + + + + + + + + + + + + + + + + + +Mamakos, et. al. Informational [Page 16] + +RFC 2516 Transmitting PPP Over Ethernet February 1999 + + +Full Copyright Statement + + Copyright (C) The Internet Society (1999). All Rights Reserved. + + This document and translations of it may be copied and furnished to + others, and derivative works that comment on or otherwise explain it + or assist in its implementation may be prepared, copied, published + and distributed, in whole or in part, without restriction of any + kind, provided that the above copyright notice and this paragraph are + included on all such copies and derivative works. However, this + document itself may not be modified in any way, such as by removing + the copyright notice or references to the Internet Society or other + Internet organizations, except as needed for the purpose of + developing Internet standards in which case the procedures for + copyrights defined in the Internet Standards process must be + followed, or as required to translate it into languages other than + English. + + The limited permissions granted above are perpetual and will not be + revoked by the Internet Society or its successors or assigns. + + This document and the information contained herein is provided on an + "AS IS" basis and THE INTERNET SOCIETY AND THE INTERNET ENGINEERING + TASK FORCE DISCLAIMS ALL WARRANTIES, EXPRESS OR IMPLIED, INCLUDING + BUT NOT LIMITED TO ANY WARRANTY THAT THE USE OF THE INFORMATION + HEREIN WILL NOT INFRINGE ANY RIGHTS OR ANY IMPLIED WARRANTIES OF + MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. + + + + + + + + + + + + + + + + + + + + + + + + +Mamakos, et. al. Informational [Page 17] + |