summaryrefslogtreecommitdiff
path: root/accel-pppd/ctrl
diff options
context:
space:
mode:
Diffstat (limited to 'accel-pppd/ctrl')
-rw-r--r--accel-pppd/ctrl/CMakeLists.txt1
-rw-r--r--accel-pppd/ctrl/ipoe/CMakeLists.txt26
-rw-r--r--accel-pppd/ctrl/ipoe/backup.c217
-rw-r--r--accel-pppd/ctrl/ipoe/dhcpv4.c756
-rw-r--r--accel-pppd/ctrl/ipoe/dhcpv4.h106
-rw-r--r--accel-pppd/ctrl/ipoe/dhcpv4_options.c290
l---------accel-pppd/ctrl/ipoe/if_ipoe.h1
-rw-r--r--accel-pppd/ctrl/ipoe/ipoe.c1159
-rw-r--r--accel-pppd/ctrl/ipoe/ipoe.h84
-rw-r--r--accel-pppd/ctrl/ipoe/ipoe_netlink.c480
-rw-r--r--accel-pppd/ctrl/ipoe/lua.c286
-rw-r--r--accel-pppd/ctrl/ipoe/lua_lpack.c271
-rw-r--r--accel-pppd/ctrl/l2tp/l2tp.c30
-rw-r--r--accel-pppd/ctrl/pppoe/pppoe.c36
-rw-r--r--accel-pppd/ctrl/pptp/pptp.c36
15 files changed, 3731 insertions, 48 deletions
diff --git a/accel-pppd/ctrl/CMakeLists.txt b/accel-pppd/ctrl/CMakeLists.txt
index 6b37bc4..9b6a11d 100644
--- a/accel-pppd/ctrl/CMakeLists.txt
+++ b/accel-pppd/ctrl/CMakeLists.txt
@@ -1,3 +1,4 @@
ADD_SUBDIRECTORY(pptp)
ADD_SUBDIRECTORY(pppoe)
ADD_SUBDIRECTORY(l2tp)
+ADD_SUBDIRECTORY(ipoe)
diff --git a/accel-pppd/ctrl/ipoe/CMakeLists.txt b/accel-pppd/ctrl/ipoe/CMakeLists.txt
new file mode 100644
index 0000000..fdcb3a4
--- /dev/null
+++ b/accel-pppd/ctrl/ipoe/CMakeLists.txt
@@ -0,0 +1,26 @@
+INCLUDE_DIRECTORIES(${CMAKE_CURRENT_SOURCE_DIR})
+
+SET(sources
+ ipoe.c
+ dhcpv4.c
+ dhcpv4_options.c
+ ipoe_netlink.c
+ backup.c
+)
+
+IF (LUA)
+ include(FindLua51)
+ IF (NOT LUA51_FOUND)
+ MESSAGE(FATAL_ERROR "lua not found")
+ ENDIF (NOT LUA51_FOUND)
+ INCLUDE_DIRECTORIES(${LUA_INCLUDE_DIR})
+ ADD_DEFINITIONS(-DUSE_LUA)
+ SET(sources ${sources} lua.c lua_lpack.c)
+ENDIF (LUA)
+
+ADD_LIBRARY(ipoe SHARED ${sources})
+IF (LUA)
+ TARGET_LINK_LIBRARIES(ipoe ${LUA_LIBRARIES})
+ENDIF(LUA)
+
+INSTALL(TARGETS ipoe LIBRARY DESTINATION lib/accel-ppp)
diff --git a/accel-pppd/ctrl/ipoe/backup.c b/accel-pppd/ctrl/ipoe/backup.c
new file mode 100644
index 0000000..8347a4e
--- /dev/null
+++ b/accel-pppd/ctrl/ipoe/backup.c
@@ -0,0 +1,217 @@
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <netinet/in.h>
+#include <net/ethernet.h>
+
+#include "triton.h"
+#include "events.h"
+#include "log.h"
+#include "memdebug.h"
+
+#include "ipoe.h"
+#include "backup.h"
+#include "ap_session_backup.h"
+
+#define IPOE_TAG_HWADDR 1
+#define IPOE_TAG_CLIENT_ID 2
+#define IPOE_TAG_AGENT_CIRCUIT_ID 3
+#define IPOE_TAG_AGENT_REMOTE_ID 4
+#define IPOE_TAG_XID 5
+#define IPOE_TAG_GIADDR 6
+#define IPOE_TAG_CALLING_SID 7
+#define IPOE_TAG_CALLED_SID 8
+#define IPOE_TAG_IFNAME 9
+
+#define IPOE_TAG_IFINDEX 100
+
+
+#define add_tag(id, data, size) if (!backup_add_tag(m, id, 0, data, size)) return -1;
+#define add_tag_i(id, data, size) if (!backup_add_tag(m, id, 1, data, size)) return -1;
+
+static LIST_HEAD(ds_list);
+
+static void restore_complete(void);
+
+#ifdef USE_BACKUP
+static int session_save(struct ap_session *ses, struct backup_mod *m)
+{
+ struct ipoe_session *conn = container_of(ses, typeof(*conn), ses);
+
+ add_tag(IPOE_TAG_HWADDR, conn->hwaddr, 6);
+ add_tag(IPOE_TAG_CALLING_SID, ses->ctrl->calling_station_id, strlen(ses->ctrl->calling_station_id));
+ add_tag(IPOE_TAG_CALLED_SID, ses->ctrl->called_station_id, strlen(ses->ctrl->called_station_id));
+ add_tag(IPOE_TAG_XID, &conn->xid, 4);
+ add_tag(IPOE_TAG_GIADDR, &conn->giaddr, 4);
+
+ if (conn->client_id)
+ add_tag(IPOE_TAG_CLIENT_ID, conn->client_id->data, conn->client_id->len);
+ if (conn->agent_circuit_id)
+ add_tag(IPOE_TAG_AGENT_CIRCUIT_ID, conn->agent_circuit_id->data, conn->agent_circuit_id->len);
+ if (conn->agent_circuit_id)
+ add_tag(IPOE_TAG_AGENT_REMOTE_ID, conn->agent_remote_id->data, conn->agent_remote_id->len);
+
+ add_tag(IPOE_TAG_IFNAME, conn->serv->ifname, strlen(conn->serv->ifname) + 1);
+
+ add_tag_i(IPOE_TAG_IFINDEX, &conn->ifindex, 4);
+
+ return 0;
+}
+
+static int session_restore(struct ap_session *ses, struct backup_mod *m)
+{
+ struct ipoe_session *conn = container_of(ses, typeof(*conn), ses);
+
+
+ return 0;
+}
+
+static void set_dhcpv4_opt(struct dhcp_opt **opt, struct backup_tag *t, uint8_t **ptr)
+{
+ *opt = (struct dhcp_opt *)(*ptr);
+ (*opt)->len = t->size;
+ memcpy((*opt)->data, t->data, t->size);
+ (*ptr) += sizeof(**opt) + t->size;
+}
+
+static struct ap_session *ctrl_restore(struct backup_mod *m)
+{
+ struct backup_tag *t;
+ struct ipoe_session *ses;
+ struct ipoe_serv *serv;
+ struct backup_tag *ifname = NULL;
+ int dlen = 0;
+ uint8_t *ptr;
+ struct ipoe_session_info *info;
+
+ //if (!m->data->internal)
+ // return NULL;
+
+ list_for_each_entry(t, &m->tag_list, entry) {
+ switch(t->id) {
+ case IPOE_TAG_CLIENT_ID:
+ case IPOE_TAG_AGENT_CIRCUIT_ID:
+ case IPOE_TAG_AGENT_REMOTE_ID:
+ dlen += sizeof(struct dhcp_opt) + t->size;
+ break;
+ case IPOE_TAG_IFNAME:
+ ifname = t;
+ break;
+ }
+ }
+
+ if (!ifname)
+ return NULL;
+
+ serv = ipoe_find_serv((char *)ifname->data);
+ if (!serv)
+ return NULL;
+
+ ses = ipoe_session_alloc();
+ if (!ses)
+ return NULL;
+
+ if (dlen)
+ ses->data = _malloc(dlen);
+
+ ptr = ses->data;
+
+ list_for_each_entry(t, &m->tag_list, entry) {
+ switch(t->id) {
+ case IPOE_TAG_HWADDR:
+ memcpy(ses->hwaddr, t->data, 6);
+ break;
+ case IPOE_TAG_CALLING_SID:
+ ses->ctrl.calling_station_id = _malloc(t->size + 1);
+ memcpy(ses->ctrl.calling_station_id, t->data, t->size);
+ ses->ctrl.calling_station_id[t->size] = 0;
+ break;
+ case IPOE_TAG_CALLED_SID:
+ ses->ctrl.called_station_id = _malloc(t->size + 1);
+ memcpy(ses->ctrl.called_station_id, t->data, t->size);
+ ses->ctrl.called_station_id[t->size] = 0;
+ break;
+ case IPOE_TAG_XID:
+ ses->xid = *(uint32_t *)t->data;
+ break;
+ case IPOE_TAG_GIADDR:
+ ses->giaddr = *(uint32_t *)t->data;
+ break;
+ case IPOE_TAG_CLIENT_ID:
+ set_dhcpv4_opt(&ses->client_id, t, &ptr);
+ break;
+ case IPOE_TAG_AGENT_CIRCUIT_ID:
+ set_dhcpv4_opt(&ses->agent_circuit_id, t, &ptr);
+ break;
+ case IPOE_TAG_AGENT_REMOTE_ID:
+ set_dhcpv4_opt(&ses->agent_remote_id, t, &ptr);
+ break;
+ case IPOE_TAG_IFINDEX:
+ ses->ifindex = *(uint32_t *)t->data;
+ break;
+ }
+ }
+
+ ses->serv = serv;
+
+ triton_context_register(&ses->ctx, &ses->ses);
+ triton_context_wakeup(&ses->ctx);
+
+ pthread_mutex_lock(&serv->lock);
+ list_add_tail(&ses->entry, &serv->sessions);
+ pthread_mutex_unlock(&serv->lock);
+
+ if (ses->ifindex != -1) {
+ list_for_each_entry(info, &ds_list, entry) {
+ if (info->ifindex == ses->ifindex) {
+ list_del(&info->entry);
+ _free(info);
+ break;
+ }
+ }
+ }
+
+ return &ses->ses;
+}
+
+static struct backup_module mod = {
+ .id = MODID_IPOE,
+ .save = session_save,
+ .restore = session_restore,
+ .ctrl_restore = ctrl_restore,
+ .restore_complete = restore_complete,
+};
+#endif
+
+static void dump_sessions(void)
+{
+ ipoe_nl_get_sessions(&ds_list);
+
+#ifndef USE_BACKUP
+ restore_complete();
+#endif
+}
+
+static void restore_complete(void)
+{
+ struct ipoe_session_info *info;
+
+ while (!list_empty(&ds_list)) {
+ info = list_entry(ds_list.next, typeof(*info), entry);
+ ipoe_nl_delete(info->ifindex);
+ list_del(&info->entry);
+ _free(info);
+ }
+}
+
+static void init(void)
+{
+ dump_sessions();
+
+#ifdef USE_BACKUP
+ backup_register_module(&mod);
+#endif
+}
+
+DEFINE_INIT(100, init);
+
diff --git a/accel-pppd/ctrl/ipoe/dhcpv4.c b/accel-pppd/ctrl/ipoe/dhcpv4.c
new file mode 100644
index 0000000..f1a8876
--- /dev/null
+++ b/accel-pppd/ctrl/ipoe/dhcpv4.c
@@ -0,0 +1,756 @@
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdio.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 <netinet/ip.h>
+#include <netinet/udp.h>
+#include <netpacket/packet.h>
+#include <arpa/inet.h>
+#include <linux/if.h>
+
+#include "events.h"
+#include "list.h"
+#include "triton.h"
+#include "log.h"
+#include "mempool.h"
+#include "memdebug.h"
+#include "ap_session.h"
+#include "ipdb.h"
+
+#include "dhcpv4.h"
+
+#define DHCP_SERV_PORT 67
+#define DHCP_CLIENT_PORT 68
+#define DHCP_MAGIC "\x63\x82\x53\x63"
+
+
+#define BUF_SIZE 4096
+
+
+static int conf_verbose;
+static in_addr_t conf_dns1;
+static in_addr_t conf_dns2;
+
+static mempool_t pack_pool;
+static mempool_t opt_pool;
+
+static int dhcpv4_read(struct triton_md_handler_t *h);
+
+static struct dhcpv4_iprange *parse_range(const char *str)
+{
+ unsigned int f1,f2,f3,f4,m,n, mask, start, end, len;
+ struct dhcpv4_iprange *r;
+
+ n = sscanf(str, "%u.%u.%u.%u/%u", &f1, &f2, &f3, &f4, &m);
+
+ if (n != 5)
+ goto parse_err;
+ if (f1 > 255)
+ goto parse_err;
+ if (f2 > 255)
+ goto parse_err;
+ if (f3 > 255)
+ goto parse_err;
+ if (f4 > 255)
+ goto parse_err;
+ if (m == 0 || m > 30)
+ goto parse_err;
+
+ start = (f1 << 24) | (f2 << 16) | (f3 << 8) | f4;
+ mask = ~((1 << (32 - m)) - 1);
+ start = start & mask;
+ end = start | ~mask;
+
+ len = (end - start - 1) / (8 * sizeof(long)) + 1;
+
+ r = _malloc(sizeof(*r) + len * sizeof(long));
+ memset(r, 0, sizeof(*r));
+ memset(r->free, 0xff, len * sizeof(long));
+ r->routerip = start + 1;
+ r->startip = start;
+ r->mask = m;
+ r->len = len;
+ pthread_mutex_init(&r->lock, NULL);
+
+ end -= start;
+ r->free[(end - 1) / ( 8 * sizeof(long))] &= (1 << ((end - 1) % (8 * sizeof(long)) + 1)) - 1;
+ r->free[0] &= ~3;
+
+ return r;
+
+parse_err:
+ log_emerg("dhcpv4: failed to parse range=%s\n", str);
+ return NULL;
+}
+
+struct dhcpv4_serv *dhcpv4_create(struct triton_context_t *ctx, const char *ifname, const char *opt)
+{
+ struct dhcpv4_serv *serv;
+ int sock, raw_sock;
+ struct sockaddr_in addr;
+ struct sockaddr_ll ll_addr;
+ struct ifreq ifr;
+ int f = 1;
+ char *str0, *str, *ptr1, *ptr2;
+ int end;
+
+ memset(&ifr, 0, sizeof(ifr));
+
+ strncpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
+ if (ioctl(sock_fd, SIOCGIFINDEX, &ifr)) {
+ log_error("dhcpv4(%s): ioctl(SIOCGIFINDEX): %s\n", ifname, strerror(errno));
+ return NULL;
+ }
+
+ raw_sock = socket(AF_PACKET, SOCK_RAW, ntohs(ETH_P_IP));
+ if (raw_sock < 0) {
+ log_error("dhcpv4: packet socket is not supported by kernel\n");
+ return NULL;
+ }
+
+ memset(&ll_addr, 0, sizeof(ll_addr));
+ ll_addr.sll_family = AF_PACKET;
+ ll_addr.sll_ifindex = ifr.ifr_ifindex;
+ ll_addr.sll_protocol = ntohs(ETH_P_IP);
+
+ if (bind(raw_sock, (struct sockaddr *)&ll_addr, sizeof(ll_addr))) {
+ log_error("dhcpv4(%s): bind: %s\n", ifname, strerror(errno));
+ close(raw_sock);
+ return NULL;
+ }
+
+ memset(&addr, 0, sizeof(addr));
+
+ addr.sin_family = AF_INET;
+ addr.sin_port = htons(DHCP_SERV_PORT);
+ addr.sin_addr.s_addr = htonl(INADDR_ANY);
+
+ sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
+
+ if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &f, sizeof(f)))
+ log_error("setsockopt(SO_REUSEADDR): %s\n", strerror(errno));
+
+
+ if (setsockopt(sock, SOL_SOCKET, SO_BROADCAST, &f, sizeof(f))) {
+ log_error("setsockopt(SO_BROADCAST): %s\n", strerror(errno));
+ goto out_err;
+ }
+
+ if (bind(sock, &addr, sizeof(addr))) {
+ log_error("bind: %s\n", strerror(errno));
+ goto out_err;
+ }
+
+ if (setsockopt(sock, SOL_SOCKET, SO_BINDTODEVICE, ifname, strlen(ifname))) {
+ log_error("setsockopt(SO_BINDTODEVICE): %s\n", strerror(errno));
+ goto out_err;
+ }
+
+ if (ioctl(sock, SIOCGIFHWADDR, &ifr)) {
+ log_error("dhcpv4(%s): ioctl(SIOCGIFHWADDR): %s\n", ifname, strerror(errno));
+ goto out_err;
+ }
+
+ fcntl(raw_sock, F_SETFL, O_NONBLOCK);
+ fcntl(raw_sock, F_SETFD, fcntl(raw_sock, F_GETFD) | FD_CLOEXEC);
+
+ fcntl(sock, F_SETFL, O_NONBLOCK);
+ fcntl(sock, F_SETFD, fcntl(sock, F_GETFD) | FD_CLOEXEC);
+
+ serv = _malloc(sizeof(*serv));
+ memset(serv, 0, sizeof(*serv));
+
+ memcpy(serv->hwaddr, ifr.ifr_hwaddr.sa_data, ETH_ALEN);
+
+ serv->ctx = ctx;
+ serv->hnd.fd = sock;
+ serv->hnd.read = dhcpv4_read;
+ serv->raw_sock = raw_sock;
+
+ str0 = strchr(opt, ',');
+ if (str0) {
+ str0 = _strdup(str0 + 1);
+ str = str0;
+
+ while (1) {
+ for (ptr1 = str + 1; *ptr1 && *ptr1 != '='; ptr1++);
+
+ if (!*ptr1)
+ break;
+
+ *ptr1 = 0;
+
+ for (ptr2 = ++ptr1; *ptr2 && *ptr2 != ','; ptr2++);
+
+ end = *ptr2 == 0;
+
+ if (!end)
+ *ptr2 = 0;
+
+ if (ptr2 == ptr1)
+ break;
+
+ if (strcmp(str, "range") == 0)
+ serv->range = parse_range(ptr1);
+
+ if (end)
+ break;
+
+ str = ptr2 + 1;
+ }
+
+ _free(str0);
+ }
+
+ triton_md_register_handler(ctx, &serv->hnd);
+ triton_md_enable_handler(&serv->hnd, MD_MODE_READ);
+
+ return serv;
+
+out_err:
+ close(raw_sock);
+ close(sock);
+ return NULL;
+}
+
+void dhcpv4_free(struct dhcpv4_serv *serv)
+{
+ triton_md_unregister_handler(&serv->hnd);
+ close(serv->hnd.fd);
+ if (serv->range)
+ _free(serv->range);
+ _free(serv);
+}
+
+void dhcpv4_print_packet(struct dhcpv4_packet *pack, void (*print)(const char *fmt, ...))
+{
+ const char *msg_name[] = {"Discover", "Offer", "Request", "Decline", "Ack", "Nak", "Release", "Inform"};
+
+ print("[DHCPv4 %s xid=%x ", msg_name[pack->msg_type - 1], pack->hdr->xid);
+
+ if (pack->hdr->ciaddr)
+ print("ciaddr=%i.%i.%i.%i ",
+ pack->hdr->ciaddr & 0xff,
+ (pack->hdr->ciaddr >> 8) & 0xff,
+ (pack->hdr->ciaddr >> 16) & 0xff,
+ (pack->hdr->ciaddr >> 24) & 0xff);
+
+ if (pack->hdr->yiaddr)
+ print("yiaddr=%i.%i.%i.%i ",
+ pack->hdr->yiaddr & 0xff,
+ (pack->hdr->yiaddr >> 8) & 0xff,
+ (pack->hdr->yiaddr >> 16) & 0xff,
+ (pack->hdr->yiaddr >> 24) & 0xff);
+
+ if (pack->hdr->siaddr)
+ print("siaddr=%i.%i.%i.%i ",
+ pack->hdr->siaddr & 0xff,
+ (pack->hdr->siaddr >> 8) & 0xff,
+ (pack->hdr->siaddr >> 16) & 0xff,
+ (pack->hdr->siaddr >> 24) & 0xff);
+
+ if (pack->hdr->giaddr)
+ print("giaddr=%i.%i.%i.%i ",
+ pack->hdr->giaddr & 0xff,
+ (pack->hdr->giaddr >> 8) & 0xff,
+ (pack->hdr->giaddr >> 16) & 0xff,
+ (pack->hdr->giaddr >> 24) & 0xff);
+
+ print("chaddr=%02x:%02x:%02x:%02x:%02x:%02x ",
+ pack->hdr->chaddr[0],
+ pack->hdr->chaddr[1],
+ pack->hdr->chaddr[2],
+ pack->hdr->chaddr[3],
+ pack->hdr->chaddr[4],
+ pack->hdr->chaddr[5],
+ pack->hdr->chaddr[6]);
+
+ dhcpv4_print_options(pack, print);
+
+ print("]\n");
+}
+
+static int parse_opt82(struct dhcpv4_packet *pack, struct dhcpv4_option *opt)
+{
+ uint8_t *ptr = opt->data;
+ uint8_t *endptr = ptr + opt->len;
+ int type, len;
+ struct dhcpv4_option *opt1;
+
+ while (ptr < endptr) {
+ type = *ptr++;
+ len = *ptr++;
+ if (ptr + len > endptr)
+ return -1;
+ if (type == 1 || type == 2) {
+ opt1 = mempool_alloc(opt_pool);
+ if (!opt1) {
+ log_emerg("out of memory\n");
+ return -1;
+ }
+
+ opt1->type = type;
+ opt1->len = len;
+ opt1->data = ptr;
+
+ if (type == 1)
+ pack->agent_circuit_id = opt1;
+ else
+ pack->agent_remote_id = opt1;
+ }
+
+ ptr += len;
+ }
+
+ return 0;
+}
+
+static int dhcpv4_parse_packet(struct dhcpv4_packet *pack, int len)
+{
+ struct dhcpv4_option *opt;
+ uint8_t *ptr, *endptr = pack->data + len;
+
+ if (len < sizeof(struct dhcpv4_hdr)) {
+ if (conf_verbose)
+ log_warn("dhcpv4: short packet received\n");
+ return -1;
+ }
+
+ if (pack->hdr->op != DHCP_OP_REQUEST)
+ return -1;
+
+ if (pack->hdr->htype != 1)
+ return -1;
+
+ if (pack->hdr->hlen != 6)
+ return -1;
+
+ if (memcmp(pack->hdr->magic, DHCP_MAGIC, 4))
+ return -1;
+
+ ptr = pack->data + sizeof(struct dhcpv4_hdr);
+
+ while (ptr < endptr) {
+ if (*ptr == 0) {
+ ptr++;
+ continue;
+ }
+
+ if (*ptr == 0xff)
+ break;
+
+ opt = mempool_alloc(opt_pool);
+ if (!opt) {
+ log_emerg("out of memory\n");
+ return -1;
+ }
+ memset(opt, 0, sizeof(*opt));
+ opt->type = *ptr++;
+ opt->len = *ptr++;
+ opt->data = ptr;
+ ptr += opt->len;
+
+ if (ptr > endptr)
+ return -1;
+
+ list_add_tail(&opt->entry, &pack->options);
+
+ if (opt->type == 53)
+ pack->msg_type = opt->data[0];
+ else if (opt->type == 82)
+ parse_opt82(pack, opt);
+ else if (opt->type == 50)
+ pack->request_ip = *(uint32_t *)opt->data;
+ else if (opt->type == 54)
+ pack->server_id = *(uint32_t *)opt->data;
+ }
+
+ if (pack->msg_type == 0 || pack->msg_type > 8)
+ return -1;
+
+ if (dhcpv4_check_options(pack))
+ return -1;
+
+ /*if (conf_verbose) {
+ log_info2("recv ");
+ print_packet(pack, log_info2);
+ }*/
+
+ return 0;
+}
+
+static struct dhcpv4_packet *dhcpv4_packet_alloc()
+{
+ struct dhcpv4_packet *pack = mempool_alloc(pack_pool);
+
+ if (!pack)
+ return NULL;
+
+ memset(pack, 0, sizeof(*pack));
+
+ INIT_LIST_HEAD(&pack->options);
+
+ pack->hdr = (struct dhcpv4_hdr *)pack->data;
+ pack->ptr = (uint8_t *)(pack->hdr + 1);
+
+ memcpy(pack->hdr->magic, DHCP_MAGIC, 4);
+
+ return pack;
+}
+
+static int dhcpv4_read(struct triton_md_handler_t *h)
+{
+ struct dhcpv4_packet *pack;
+ struct dhcpv4_serv *serv = container_of(h, typeof(*serv), hnd);
+ struct sockaddr_in addr;
+ socklen_t len;
+ int n;
+
+ while (1) {
+ pack = dhcpv4_packet_alloc();
+ if (!pack) {
+ log_emerg("out of memory\n");
+ return 1;
+ }
+
+ len = sizeof(addr);
+ n = recvfrom(h->fd, pack->data, BUF_SIZE, 0, &addr, &len);
+ if (n == -1) {
+ mempool_free(pack);
+ if (errno == EAGAIN)
+ return 0;
+ log_error("dhcpv4: recv: %s\n", strerror(errno));
+ continue;
+ }
+
+ if (dhcpv4_parse_packet(pack, n)) {
+ dhcpv4_packet_free(pack);
+ continue;
+ }
+
+ if (serv->recv)
+ serv->recv(serv, pack);
+ }
+}
+
+uint16_t ip_csum(uint16_t *buf, int len)
+{
+ uint32_t sum=0;
+ int i;
+
+ for (i=0; i < len; i += 2)
+ sum += *buf++;
+
+ // take only 16 bits out of the 32 bit sum and add up the carries
+ while (sum >> 16)
+ sum = (sum & 0xffff) + (sum >> 16);
+
+ // one's complement the result
+ sum = ~sum;
+
+ return sum & 0xffff;
+}
+
+
+static int dhcpv4_send_raw(struct dhcpv4_serv *serv, struct dhcpv4_packet *pack, in_addr_t saddr, in_addr_t daddr)
+{
+ uint8_t hdr[sizeof(struct ether_header) + sizeof(struct iphdr) + sizeof(struct udphdr)];
+ struct ether_header *eth = (struct ether_header *)hdr;
+ struct iphdr *ip = (struct iphdr *)(eth + 1);
+ struct udphdr *udp = (struct udphdr *)(ip + 1);
+ int len = pack->ptr - pack->data;
+ struct iovec iov[2];
+
+ memcpy(eth->ether_dhost, pack->hdr->chaddr, ETH_ALEN);
+ memcpy(eth->ether_shost, serv->hwaddr, ETH_ALEN);
+ eth->ether_type = htons(ETH_P_IP);
+
+ ip->ihl = 5;
+ ip->version = 4;
+ ip->tos = 0x10;
+ ip->tot_len = ntohs(sizeof(*ip) + sizeof(*udp) + len);
+ ip->id = 0;
+ ip->frag_off = 0;
+ ip->ttl = 128;
+ ip->protocol = IPPROTO_UDP;
+ ip->check = 0;
+ ip->saddr = saddr;
+ ip->daddr = daddr;
+ ip->check = ip_csum((uint16_t *)ip, 20);
+
+ udp->source = ntohs(DHCP_SERV_PORT);
+ udp->dest = ntohs(DHCP_CLIENT_PORT);
+ udp->len = htons(sizeof(*udp) + len);
+ udp->check = 0;
+
+ iov[0].iov_base = hdr;
+ iov[0].iov_len = sizeof(hdr);
+ iov[1].iov_base = pack->data;
+ iov[1].iov_len = len;
+
+ len = writev(serv->raw_sock, iov, 2);
+
+ if (len < 0)
+ return -1;
+
+ return 0;
+}
+
+static int dhcpv4_send_udp(struct dhcpv4_serv *serv, struct dhcpv4_packet *pack)
+{
+ struct sockaddr_in addr;
+ int n;
+ int len = pack->ptr - pack->data;
+
+ memset(&addr, 0, sizeof(addr));
+ addr.sin_family = AF_INET;
+ addr.sin_port = htons(DHCP_CLIENT_PORT);
+ addr.sin_addr.s_addr = pack->hdr->giaddr;
+
+ n = sendto(serv->hnd.fd, pack->data, len, 0, (struct sockaddr *)&addr, sizeof(addr));
+ if (n != len)
+ return -1;
+
+ return 0;
+}
+
+static int dhcpv4_send(struct dhcpv4_serv *serv, struct dhcpv4_packet *pack, in_addr_t saddr, in_addr_t daddr)
+{
+ if (pack->hdr->giaddr)
+ return dhcpv4_send_udp(serv, pack);
+
+ return dhcpv4_send_raw(serv, pack, saddr, daddr);
+}
+
+void dhcpv4_packet_free(struct dhcpv4_packet *pack)
+{
+ struct dhcpv4_option *opt;
+
+ while (!list_empty(&pack->options)) {
+ opt = list_entry(pack->options.next, typeof(*opt), entry);
+ list_del(&opt->entry);
+ mempool_free(opt);
+ }
+
+ if (pack->agent_circuit_id)
+ mempool_free(pack->agent_circuit_id);
+
+ if (pack->agent_remote_id)
+ mempool_free(pack->agent_remote_id);
+
+ mempool_free(pack);
+}
+
+int dhcpv4_packet_add_opt(struct dhcpv4_packet *pack, int type, const void *data, int len)
+{
+ struct dhcpv4_option *opt = mempool_alloc(opt_pool);
+
+ if (!opt) {
+ log_emerg("out of memory\n");
+ return -1;
+ }
+
+ *pack->ptr++ = type;
+ *pack->ptr++ = len;
+
+ opt->type = type;
+ opt->len = len;
+ opt->data = pack->ptr;
+ pack->ptr += len;
+
+ memcpy(opt->data, data, len);
+
+ list_add_tail(&opt->entry, &pack->options);
+
+ return 0;
+}
+
+int dhcpv4_send_reply(int msg_type, struct dhcpv4_serv *serv, struct dhcpv4_packet *req, uint32_t yiaddr, uint32_t siaddr, uint32_t mask, int lease_time)
+{
+ struct dhcpv4_packet *pack;
+ int val, r;
+ struct dns {
+ in_addr_t dns1;
+ in_addr_t dns2;
+ } dns;
+
+ pack = dhcpv4_packet_alloc();
+ if (!pack) {
+ log_emerg("out of memory\n");
+ return -1;
+ }
+
+ memcpy(pack->hdr, req->hdr, sizeof(*req->hdr));
+
+ pack->hdr->op = DHCP_OP_REPLY;
+ pack->hdr->ciaddr = 0;
+ pack->hdr->yiaddr = yiaddr;
+ if (msg_type == DHCPOFFER)
+ pack->hdr->siaddr = siaddr;
+ else
+ pack->hdr->siaddr = 0;
+
+ if (dhcpv4_packet_add_opt(pack, 53, &msg_type, 1))
+ goto out_err;
+
+ if (dhcpv4_packet_add_opt(pack, 54, &siaddr, 4))
+ goto out_err;
+
+ val = ntohl(lease_time);
+ if (dhcpv4_packet_add_opt(pack, 51, &val, 4))
+ goto out_err;
+
+ if (dhcpv4_packet_add_opt(pack, 3, &siaddr, 4))
+ goto out_err;
+
+ val = htonl(~((1 << (32 - mask)) - 1));
+ if (dhcpv4_packet_add_opt(pack, 1, &val, 4))
+ goto out_err;
+
+ if (conf_dns1 && conf_dns2) {
+ dns.dns1 = conf_dns1;
+ dns.dns2 = conf_dns2;
+ if (dhcpv4_packet_add_opt(pack, 6, &dns, 8))
+ goto out_err;
+ } else if (conf_dns1) {
+ if (dhcpv4_packet_add_opt(pack, 6, &conf_dns1, 4))
+ goto out_err;
+ }
+
+ *pack->ptr++ = 255;
+
+ if (conf_verbose) {
+ pack->msg_type = msg_type;
+ log_ppp_info2("send ");
+ dhcpv4_print_packet(pack, log_ppp_info2);
+ }
+
+ r = dhcpv4_send(serv, pack, siaddr, yiaddr);
+
+ dhcpv4_packet_free(pack);
+
+ return r;
+
+out_err:
+ dhcpv4_packet_free(pack);
+ return -1;
+}
+
+int dhcpv4_send_nak(struct dhcpv4_serv *serv, struct dhcpv4_packet *req)
+{
+ struct dhcpv4_packet *pack;
+ int val, r;
+
+ pack = dhcpv4_packet_alloc();
+ if (!pack) {
+ log_emerg("out of memory\n");
+ return -1;
+ }
+
+ memcpy(pack->hdr, req->hdr, sizeof(*req->hdr));
+
+ pack->hdr->op = DHCP_OP_REPLY;
+ pack->hdr->ciaddr = 0;
+ pack->hdr->yiaddr = 0;
+ pack->hdr->siaddr = 0;
+
+ val = DHCPNAK;
+ if (dhcpv4_packet_add_opt(pack, 53, &val, 1))
+ goto out_err;
+
+ *pack->ptr++ = 255;
+
+ if (conf_verbose) {
+ pack->msg_type = DHCPNAK;
+ log_info2("send ");
+ dhcpv4_print_packet(pack, log_info2);
+ }
+
+ r = dhcpv4_send(serv, pack, 0, 0xffffffff);
+
+ dhcpv4_packet_free(pack);
+
+ return r;
+
+out_err:
+ dhcpv4_packet_free(pack);
+ return -1;
+
+ return 0;
+}
+
+int dhcpv4_get_ip(struct dhcpv4_serv *serv, uint32_t *yiaddr, uint32_t *siaddr, int *mask)
+{
+ int i, k;
+
+ if (!serv->range)
+ return 0;
+
+ pthread_mutex_lock(&serv->range->lock);
+
+ while (1) {
+ for (i = serv->range->pos; i < serv->range->len; i++) {
+ k = ffsl(serv->range->free[i]);
+ if (k) {
+ serv->range->free[i] &= ~(1 << (k - 1));
+ serv->range->pos = i;
+ pthread_mutex_unlock(&serv->range->lock);
+ *yiaddr = htonl(serv->range->startip + i * 8 * sizeof(long) + k - 1);
+ *siaddr = htonl(serv->range->routerip);
+ *mask = serv->range->mask;
+ return 1;
+ }
+ }
+
+ if (serv->range->pos == 0)
+ break;
+
+ serv->range->pos = 0;
+ }
+
+ pthread_mutex_unlock(&serv->range->lock);
+ return 0;
+}
+
+void dhcpv4_put_ip(struct dhcpv4_serv *serv, uint32_t ip)
+{
+ int n = ntohl(ip) - serv->range->startip;
+ pthread_mutex_lock(&serv->range->lock);
+ serv->range->free[n / (8 * sizeof(long))] |= 1 << (n % (8 * sizeof(long)));
+ pthread_mutex_unlock(&serv->range->lock);
+}
+
+static void load_config()
+{
+ const char *opt;
+
+ opt = conf_get_opt("ipoe", "verbose");
+ if (opt)
+ conf_verbose = atoi(opt);
+
+ opt = conf_get_opt("dns", "dns1");
+ if (opt)
+ conf_dns1 = inet_addr(opt);
+
+ opt = conf_get_opt("dns", "dns2");
+ if (opt)
+ conf_dns2 = inet_addr(opt);
+}
+
+static void init()
+{
+ pack_pool = mempool_create(BUF_SIZE + sizeof(struct dhcpv4_packet));
+ opt_pool = mempool_create(sizeof(struct dhcpv4_option));
+
+ load_config();
+
+ triton_event_register_handler(EV_CONFIG_RELOAD, (triton_event_func)load_config);
+}
+
+DEFINE_INIT(100, init);
diff --git a/accel-pppd/ctrl/ipoe/dhcpv4.h b/accel-pppd/ctrl/ipoe/dhcpv4.h
new file mode 100644
index 0000000..cf3aac7
--- /dev/null
+++ b/accel-pppd/ctrl/ipoe/dhcpv4.h
@@ -0,0 +1,106 @@
+#ifndef __DHCPV4_H
+#define __DHCPV4_H
+
+#include <stdint.h>
+#include <pthread.h>
+#include "list.h"
+
+#include "triton.h"
+
+#define __packed __attribute__((packed))
+
+#define DHCP_OP_REQUEST 1
+#define DHCP_OP_REPLY 2
+
+#define DHCPDISCOVER 1
+#define DHCPOFFER 2
+#define DHCPREQUEST 3
+#define DHCPDECLINE 4
+#define DHCPACK 5
+#define DHCPNAK 6
+#define DHCPRELEASE 7
+#define DHCPINFORM 8
+
+struct dhcpv4_hdr
+{
+ uint8_t op;
+ uint8_t htype;
+ uint8_t hlen;
+ uint8_t hops;
+ uint32_t xid;
+ uint16_t sec;
+ uint16_t flags;
+ uint32_t ciaddr;
+ uint32_t yiaddr;
+ uint32_t siaddr;
+ uint32_t giaddr;
+ uint8_t chaddr[16];
+ char sname[64];
+ char file[128];
+ uint8_t magic[4];
+} __packed;
+
+struct dhcpv4_option
+{
+ struct list_head entry;
+ uint8_t type;
+ uint8_t len;
+ uint8_t *data;
+};
+
+struct dhcpv4_packet
+{
+ struct dhcpv4_hdr *hdr;
+ struct list_head options;
+ struct dhcpv4_option *client_id;
+ struct dhcpv4_option *agent_circuit_id;
+ struct dhcpv4_option *agent_remote_id;
+ uint32_t request_ip;
+ uint32_t server_id;
+ int msg_type;
+ uint8_t *ptr;
+ uint8_t data[0];
+};
+
+struct dhcpv4_iprange
+{
+ struct list_head entry;
+ uint32_t routerip;
+ uint32_t startip;
+ int mask;
+ int pos;
+ int len;
+ pthread_mutex_t lock;
+ unsigned long free[0];
+};
+
+struct dhcpv4_serv
+{
+ struct triton_context_t *ctx;
+ struct triton_md_handler_t hnd;
+ int raw_sock;
+ uint8_t hwaddr[6];
+ void (*recv)(struct dhcpv4_serv *serv, struct dhcpv4_packet *pack);
+ struct dhcpv4_iprange *range;
+};
+
+struct ap_session;
+
+struct dhcpv4_serv *dhcpv4_create(struct triton_context_t *ctx, const char *ifname, const char *opt);
+void dhcpv4_free(struct dhcpv4_serv *);
+
+
+int dhcpv4_send_reply(int msg_type, struct dhcpv4_serv *serv, struct dhcpv4_packet *req, uint32_t yiaddr, uint32_t siaddr, uint32_t mask, int lease_time);
+int dhcpv4_send_nak(struct dhcpv4_serv *serv, struct dhcpv4_packet *req);
+
+void dhcpv4_packet_free(struct dhcpv4_packet *pack);
+
+int dhcpv4_check_options(struct dhcpv4_packet *);
+void dhcpv4_print_options(struct dhcpv4_packet *, void (*)(const char *, ...));
+
+void dhcpv4_print_packet(struct dhcpv4_packet *pack, void (*print)(const char *fmt, ...));
+
+int dhcpv4_get_ip(struct dhcpv4_serv *serv, uint32_t *yiaddr, uint32_t *siaddr, int *mask);
+void dhcpv4_put_ip(struct dhcpv4_serv *serv, uint32_t ip);
+
+#endif
diff --git a/accel-pppd/ctrl/ipoe/dhcpv4_options.c b/accel-pppd/ctrl/ipoe/dhcpv4_options.c
new file mode 100644
index 0000000..82e6490
--- /dev/null
+++ b/accel-pppd/ctrl/ipoe/dhcpv4_options.c
@@ -0,0 +1,290 @@
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <ctype.h>
+#include <arpa/inet.h>
+
+#include "dhcpv4.h"
+
+struct known_option
+{
+ int type;
+ int min_len;
+ int max_len;
+ int elem_size;
+ const char *name;
+ void (*print)(const struct dhcpv4_option *opt, int elem_size, void (*print)(const char *fmt, ...));
+};
+
+static void print_int(const struct dhcpv4_option *opt, int elem_size, void (*print)(const char *fmt, ...));
+static void print_uint(const struct dhcpv4_option *opt, int elem_size, void (*print)(const char *fmt, ...));
+static void print_ip(const struct dhcpv4_option *opt, int elem_size, void (*print)(const char *fmt, ...));
+static void print_str(const struct dhcpv4_option *opt, int elem_size, void (*print)(const char *fmt, ...));
+static void print_hex(const struct dhcpv4_option *opt, int elem_size, void (*print)(const char *fmt, ...));
+static void print_route(const struct dhcpv4_option *opt, int elem_size, void (*print)(const char *fmt, ...));
+static void print_classless_route(const struct dhcpv4_option *opt, int elem_size, void (*print)(const char *fmt, ...));
+static void print_message_type(const struct dhcpv4_option *opt, int elem_size, void (*print)(const char *fmt, ...));
+static void print_request_list(const struct dhcpv4_option *opt, int elem_size, void (*print)(const char *fmt, ...));
+static void print_relay_agent(const struct dhcpv4_option *opt, int elem_size, void (*print)(const char *fmt, ...));
+
+static struct known_option options[] = {
+ { 1, 4, 4, 4, "Subnet", print_ip },
+ { 2, 4, 4, 4, "Time-Offset", print_int },
+ { 3, 4, 255, 4, "Router", print_ip },
+ { 4, 4, 255, 4, "Time-Server", print_ip },
+ { 5, 4, 255, 4, "Name-Server", print_ip },
+ { 6, 4, 255, 4, "DNS", print_ip },
+ //{ 7, 4, 255, 4, "log-server", print_ip },
+ //{ 8, 4, 255, 4, "cookie-server", print_ip },
+ //{ 9, 4, 255, 4, "lpr-server", print_ip },
+ //{ 10, 4, 255, 4, "impress-server", print_ip },
+ //{ 11, 4, 255, 4, "resourse-location", print_ip },
+ { 12, 1, 255, 1, "Host-Name", print_str },
+ //{ 13, 4, 255, 4, "impress-server", print_ip },
+ { 15, 1, 255, 1, "Domain-Name", print_str },
+ { 26, 2, 2, 2, "MTU", print_int },
+ { 28, 4, 4, 4, "Broadcast", print_ip },
+ { 33, 8, 255, 8, "Route", print_route },
+ { 42, 4, 4, 4, "NTP", print_ip },
+ { 43, 1, 255, 1, "Vendor-Specific", print_hex },
+ { 50, 4, 4, 4, "Request-IP", print_ip },
+ { 51, 4, 4, 4, "Lease-Time", print_uint },
+ { 53, 1, 1, 1, "Message-Type", print_message_type },
+ { 54, 4, 4, 4, "Server-ID", print_ip },
+ { 55, 1, 255, 1, "Request-List", print_request_list },
+ { 56, 1, 255, 1, "Message", print_str },
+ { 57, 2, 2, 2, "Max-Message-Size", print_uint },
+ { 58, 4, 4, 4, "T1", print_uint },
+ { 59, 4, 4, 4, "T2", print_uint },
+ { 60, 1, 255, 1, "Vendor-Class", print_hex },
+ { 61, 2, 255, 1, "Client-ID", print_hex },
+ { 82, 3, 255, 1, "Relay-Agent", print_relay_agent },
+ { 121, 5, 255, 1, "Classless-Route", print_classless_route },
+ { 0 },
+};
+
+int dhcpv4_check_options(struct dhcpv4_packet *pack)
+{
+ struct dhcpv4_option *opt;
+ struct known_option *kopt;
+
+ list_for_each_entry(opt, &pack->options, entry) {
+ for (kopt = options; kopt->type; kopt++) {
+ if (kopt->type != opt->type)
+ continue;
+ if (opt->len < kopt->min_len)
+ return -1;
+ if (opt->len > kopt->max_len)
+ return -1;
+ if (opt->len % kopt->elem_size != 0)
+ return -1;
+ break;
+ }
+ }
+
+ return 0;
+}
+
+void dhcpv4_print_options(struct dhcpv4_packet *pack, void (*print)(const char *fmt, ...))
+{
+ struct dhcpv4_option *opt;
+ struct known_option *kopt;
+ int n = 0;
+
+ list_for_each_entry(opt, &pack->options, entry) {
+ if (n)
+ print(" <");
+ else
+ print("<");
+ n++;
+ for (kopt = options; kopt->type && kopt->type != opt->type; kopt++);
+ if (kopt->type) {
+ print("%s ", kopt->name);
+ kopt->print(opt, kopt->elem_size, print);
+ } else {
+ print("Option-%i ");
+ print_hex(opt, 1, print);
+ }
+ print(">");
+ }
+}
+
+
+static void print_int(const struct dhcpv4_option *opt, int elem_size, void (*print)(const char *fmt, ...))
+{
+ if (opt->len == 2)
+ print("%i", ntohs(*(int16_t *)(opt->data)));
+ else
+ print("%i", ntohl(*(int32_t *)(opt->data)));
+}
+
+static void print_uint(const struct dhcpv4_option *opt, int elem_size, void (*print)(const char *fmt, ...))
+{
+ if (opt->len == 2)
+ print("%u", ntohs(*(uint16_t *)(opt->data)));
+ else
+ print("%u", ntohl(*(uint32_t *)(opt->data)));
+}
+
+static void print_ip(const struct dhcpv4_option *opt, int elem_size, void (*print)(const char *fmt, ...))
+{
+ int i, n = opt->len / elem_size;
+ uint32_t ip;
+
+ for (i = 0; i < n; i++) {
+ ip = ntohl(*(uint32_t *)(opt->data + i*elem_size));
+
+ if (i)
+ print(",");
+
+ print("%i.%i.%i.%i",
+ (ip >> 24) & 0xff,
+ (ip >> 16) & 0xff,
+ (ip >> 8) & 0xff,
+ ip & 0xff);
+ }
+}
+
+static void print_str(const struct dhcpv4_option *opt, int elem_size, void (*print)(const char *fmt, ...))
+{
+ const char *ptr = (const char *)opt->data;
+ const char *endptr = ptr + opt->len;
+
+ for(; ptr < endptr; ptr++)
+ print("%c", *ptr);
+}
+
+static void print_hex(const struct dhcpv4_option *opt, int elem_size, void (*print)(const char *fmt, ...))
+{
+ const uint8_t *ptr = opt->data;
+ const uint8_t *endptr = ptr + opt->len;
+
+ for(; ptr < endptr; ptr++)
+ print("%02x", *ptr);
+}
+
+static void print_route(const struct dhcpv4_option *opt, int elem_size, void (*print)(const char *fmt, ...))
+{
+ int i, n = opt->len / 8;
+ uint32_t ip, gw;
+
+ for (i = 0; i < n; i++) {
+ ip = ntohl(*(uint32_t *)(opt->data + i*8));
+ gw = ntohl(*(uint32_t *)(opt->data + i*8 + 4));
+
+ if (i)
+ print(",");
+
+ print("%i.%i.%i.%i via %i.%i.%i.%i",
+ (ip >> 24) & 0xff,
+ (ip >> 16) & 0xff,
+ (ip >> 8) & 0xff,
+ ip & 0xff,
+ (gw >> 24) & 0xff,
+ (gw >> 16) & 0xff,
+ (gw >> 8) & 0xff,
+ gw & 0xff);
+ }
+}
+
+static void print_message_type(const struct dhcpv4_option *opt, int elem_size, void (*print)(const char *fmt, ...))
+{
+ const char *msg_name[] = {"", "Discover", "Offer", "Request", "Decline", "Ack", "Nak", "Release", "Inform"};
+
+ print("%s", msg_name[opt->data[0]]);
+}
+
+static void print_request_list(const struct dhcpv4_option *opt, int elem_size, void (*print)(const char *fmt, ...))
+{
+ int i;
+ struct known_option *kopt;
+
+ for (i = 0; i < opt->len; i++) {
+ if (i)
+ print(",");
+ for (kopt = options; kopt->type && kopt->type != opt->data[i]; kopt++);
+ if (kopt->type)
+ print("%s", kopt->name);
+ else
+ print("%i", opt->data[i]);
+ }
+}
+
+static void print_relay_agent(const struct dhcpv4_option *opt, int elem_size, void (*print)(const char *fmt, ...))
+{
+ const uint8_t *ptr = opt->data;
+ const uint8_t *endptr = ptr + opt->len;
+ const uint8_t *endptr1;
+ int type, len;
+
+ while (ptr < endptr) {
+ if (ptr != opt->data)
+ print(" ");
+ type = *ptr++;
+ len = *ptr++;
+ /*if (ptr + len > endptr) {
+ print(" invalid");
+ return;
+ }*/
+ if (type == 1)
+ print("{Agent-Circuit-ID ");
+ else if (type == 2)
+ print("{Agent-Remote-ID ");
+ else
+ print("{Option-%i ", type);
+
+ endptr1 = ptr + len;
+ for (;ptr < endptr1; ptr++) {
+ if (!isprint(*ptr)) {
+ print("_");
+ break;
+ }
+ print("%c", *ptr);
+ }
+ for (;ptr < endptr1; ptr++)
+ print("%02x", *ptr);
+ print("}");
+ }
+}
+
+static void print_classless_route(const struct dhcpv4_option *opt, int elem_size, void (*print)(const char *fmt, ...))
+{
+ const uint8_t *ptr = opt->data;
+ const uint8_t *endptr = ptr + opt->len;
+ int mask, i, mask1 = 0;
+ uint32_t ip;
+ uint32_t gw;
+
+ while (ptr < endptr) {
+ if (ptr != opt->data)
+ print(",");
+
+ mask = *ptr++;
+ ip = ntohl(*(uint32_t *)ptr);
+ for (i = 0; i < mask; i++)
+ mask1 |= (1 << (32 - i));
+ ip &= mask1;
+ if (mask <= 8)
+ ptr++;
+ else if (mask <= 16)
+ ptr += 2;
+ else if (mask <= 24)
+ ptr += 3;
+ else
+ ptr += 4;
+ gw = ntohl(*(uint32_t *)ptr);
+ ptr += 4;
+
+ print("%i.%i.%i.%i/%i via %i.%i.%i.%i",
+ (ip >> 24) & 0xff,
+ (ip >> 16) & 0xff,
+ (ip >> 8) & 0xff,
+ ip & 0xff,
+ mask,
+ (gw >> 24) & 0xff,
+ (gw >> 16) & 0xff,
+ (gw >> 8) & 0xff,
+ gw & 0xff);
+ }
+}
diff --git a/accel-pppd/ctrl/ipoe/if_ipoe.h b/accel-pppd/ctrl/ipoe/if_ipoe.h
new file mode 120000
index 0000000..1f0e053
--- /dev/null
+++ b/accel-pppd/ctrl/ipoe/if_ipoe.h
@@ -0,0 +1 @@
+../../../drivers/ipoe/ipoe.h \ No newline at end of file
diff --git a/accel-pppd/ctrl/ipoe/ipoe.c b/accel-pppd/ctrl/ipoe/ipoe.c
new file mode 100644
index 0000000..b961ef8
--- /dev/null
+++ b/accel-pppd/ctrl/ipoe/ipoe.c
@@ -0,0 +1,1159 @@
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdarg.h>
+#include <errno.h>
+#include <string.h>
+#include <fcntl.h>
+#include <time.h>
+#include <arpa/inet.h>
+#include <netinet/in.h>
+#include <net/ethernet.h>
+#include <netinet/ip.h>
+#include <sys/socket.h>
+#include <sys/ioctl.h>
+#include <linux/if.h>
+
+#include <pcre.h>
+
+#include "events.h"
+#include "list.h"
+#include "triton.h"
+#include "log.h"
+#include "mempool.h"
+#include "utils.h"
+#include "cli.h"
+#include "ap_session.h"
+#include "pwdb.h"
+#include "ipdb.h"
+
+#include "iplink.h"
+#include "connlimit.h"
+
+#include "ipoe.h"
+
+#include "memdebug.h"
+
+#define USERNAME_IFNAME 0
+#define USERNAME_LUA 1
+
+#define MODE_L2 0
+#define MODE_L3 1
+
+static int conf_dhcpv4 = 1;
+static int conf_up = 0;
+static int conf_mode = 0;
+static int conf_shared = 1;
+//static int conf_dhcpv6;
+static int conf_username;
+static int conf_unit_cache;
+
+#ifdef USE_LUA
+static const char *conf_lua_username_func;
+#endif
+
+static int conf_offer_timeout = 3;
+static in_addr_t conf_gw_address;
+static int conf_netmask = 24;
+static int conf_lease_time = 600;
+static int conf_lease_timeout = 660;
+static int conf_verbose;
+
+static unsigned int stat_starting;
+static unsigned int stat_active;
+
+static mempool_t ses_pool;
+
+static LIST_HEAD(serv_list);
+
+struct iplink_arg
+{
+ pcre *re;
+ const char *opt;
+};
+
+struct unit_cache
+{
+ struct list_head entry;
+ int ifindex;
+};
+
+static pthread_mutex_t uc_lock = PTHREAD_MUTEX_INITIALIZER;
+static LIST_HEAD(uc_list);
+static int uc_size;
+static mempool_t uc_pool;
+
+static void ipoe_session_finished(struct ap_session *s);
+static void ipoe_drop_sessions(struct ipoe_serv *serv, struct ipoe_session *skip);
+static void ipoe_serv_close(struct triton_context_t *ctx);
+
+static struct ipoe_session *ipoe_session_lookup(struct ipoe_serv *serv, struct dhcpv4_packet *pack)
+{
+ struct ipoe_session *ses;
+ struct ipoe_session *ses1 = NULL;
+
+ list_for_each_entry(ses, &serv->sessions, entry) {
+ if (pack->hdr->giaddr != ses->giaddr)
+ continue;
+
+ if (pack->agent_circuit_id && !ses->agent_circuit_id)
+ continue;
+
+ if (pack->agent_remote_id && !ses->agent_remote_id)
+ continue;
+
+ if (!pack->agent_circuit_id && ses->agent_circuit_id)
+ continue;
+
+ if (!pack->agent_remote_id && ses->agent_remote_id)
+ continue;
+
+ if (pack->agent_circuit_id) {
+ if (pack->agent_circuit_id->len != ses->agent_circuit_id->len)
+ continue;
+ if (memcmp(pack->agent_circuit_id->data, ses->agent_circuit_id->data, pack->agent_circuit_id->len))
+ continue;
+ }
+
+ if (pack->agent_remote_id) {
+ if (pack->agent_remote_id->len != ses->agent_remote_id->len)
+ continue;
+ if (memcmp(pack->agent_remote_id->data, ses->agent_remote_id->data, pack->agent_remote_id->len))
+ continue;
+
+ return ses;
+ }
+
+ if (pack->client_id && !ses->client_id)
+ continue;
+
+ if (!pack->client_id && ses->client_id)
+ continue;
+
+
+ if (pack->client_id) {
+ if (pack->client_id->len != ses->client_id->len)
+ continue;
+ if (memcmp(pack->client_id->data, ses->client_id->data, pack->client_id->len))
+ continue;
+ }
+
+ if (memcmp(pack->hdr->chaddr, ses->hwaddr, 6))
+ continue;
+
+ ses1 = ses;
+
+ if (pack->hdr->xid != ses->xid)
+ continue;
+
+ return ses;
+ }
+
+ return ses1;
+}
+
+static void ipoe_session_timeout(struct triton_timer_t *t)
+{
+ struct ipoe_session *ses = container_of(t, typeof(*ses), timer);
+
+ triton_timer_del(t);
+
+ log_ppp_info2("session timed out\n");
+
+ ap_session_terminate(&ses->ses, TERM_LOST_CARRIER, 0);
+}
+
+static void ipoe_session_set_username(struct ipoe_session *ses)
+{
+#ifdef USE_LUA
+ if (conf_username == USERNAME_LUA) {
+ ipoe_lua_set_username(ses, conf_lua_username_func);
+ } else
+#endif
+ ses->ses.username = _strdup(ses->ses.ifname);
+}
+
+static void ipoe_session_start(struct ipoe_session *ses)
+{
+ int r;
+ char *passwd;
+ struct ifreq ifr;
+ struct unit_cache *uc;
+
+ if (ses->serv->opt_shared == 0)
+ strncpy(ses->ses.ifname, ses->serv->ifname, AP_IFNAME_LEN);
+ else if (ses->ifindex == -1) {
+ pthread_mutex_lock(&uc_lock);
+ if (!list_empty(&uc_list)) {
+ uc = list_entry(uc_list.next, typeof(*uc), entry);
+ ses->ifindex = uc->ifindex;
+ list_del(&uc->entry);
+ --uc_size;
+ pthread_mutex_unlock(&uc_lock);
+ mempool_free(uc);
+ } else {
+ pthread_mutex_unlock(&uc_lock);
+ ses->ifindex = ipoe_nl_create(0, 0, ses->serv->opt_mode == MODE_L2 ? ses->serv->ifname : NULL, ses->hwaddr);
+ if (ses->ifindex == -1) {
+ log_ppp_error("ipoe: failed to create interface\n");
+ ipoe_session_finished(&ses->ses);
+ return;
+ }
+ }
+
+ memset(&ifr, 0, sizeof(ifr));
+ ifr.ifr_ifindex = ses->ifindex;
+ if (ioctl(sock_fd, SIOCGIFNAME, &ifr, sizeof(ifr))) {
+ log_ppp_error("ipoe: failed to get interface name\n");
+ ses->ifindex = -1;
+ ipoe_session_finished(&ses->ses);
+ return;
+ }
+
+ strncpy(ses->ses.ifname, ifr.ifr_name, AP_IFNAME_LEN);
+ ses->ses.ifindex = ses->ifindex;
+ }
+
+ if (!ses->ses.username) {
+ ipoe_session_set_username(ses);
+
+ if (!ses->ses.username) {
+ ipoe_session_finished(&ses->ses);
+ return;
+ }
+ }
+
+ triton_event_fire(EV_CTRL_STARTING, &ses->ses);
+ triton_event_fire(EV_CTRL_STARTED, &ses->ses);
+
+ ap_session_starting(&ses->ses);
+
+ r = pwdb_check(&ses->ses, ses->ses.username, 0);
+ if (r == PWDB_NO_IMPL) {
+ passwd = pwdb_get_passwd(&ses->ses, ses->ses.username);
+ if (!passwd)
+ r = PWDB_DENIED;
+ else {
+ r = PWDB_SUCCESS;
+ _free(passwd);
+ }
+ }
+
+ if (r == PWDB_DENIED) {
+ if (conf_ppp_verbose)
+ log_ppp_warn("authentication failed\n");
+ ap_session_terminate(&ses->ses, TERM_AUTH_ERROR, 0);
+ return;
+ }
+
+ ses->ses.ipv4 = ipdb_get_ipv4(&ses->ses);
+ if (!ses->ses.ipv4) {
+ log_ppp_warn("no free IPv4 address\n");
+ ap_session_terminate(&ses->ses, TERM_AUTH_ERROR, 0);
+ return;
+ }
+
+ if (conf_gw_address)
+ ses->ses.ipv4->addr = conf_gw_address;
+
+ if (conf_netmask)
+ ses->ses.ipv4->mask = conf_netmask;
+ else if (!ses->ses.ipv4->mask)
+ ses->ses.ipv4->mask = 24;
+
+ if (!ses->yiaddr)
+ ses->yiaddr = ses->ses.ipv4->peer_addr;
+
+ if (!ses->siaddr)
+ ses->siaddr = ses->ses.ipv4->addr;
+
+ if (!ses->mask)
+ ses->mask = 24;
+
+ if (ses->dhcpv4_request) {
+ dhcpv4_send_reply(DHCPOFFER, ses->serv->dhcpv4, ses->dhcpv4_request, ses->yiaddr, ses->siaddr, ses->mask, conf_lease_time);
+
+ dhcpv4_packet_free(ses->dhcpv4_request);
+ ses->dhcpv4_request = NULL;
+
+ ses->timer.expire = ipoe_session_timeout;
+ ses->timer.expire_tv.tv_sec = conf_offer_timeout;
+ triton_timer_add(&ses->ctx, &ses->timer, 0);
+ } else {
+ if (ipoe_nl_modify(ses->ifindex, ses->yiaddr, ses->ses.ipv4->peer_addr, NULL, NULL))
+ ap_session_terminate(&ses->ses, TERM_NAS_ERROR, 0);
+ else
+ ap_session_activate(&ses->ses);
+ }
+}
+
+static void ipoe_session_activate(struct ipoe_session *ses)
+{
+ if (ses->ifindex != -1) {
+ if (ipoe_nl_modify(ses->ifindex, ses->yiaddr, ses->ses.ipv4->peer_addr, NULL, NULL)) {
+ ap_session_terminate(&ses->ses, TERM_NAS_ERROR, 0);
+ return;
+ }
+ }
+
+ ap_session_activate(&ses->ses);
+
+ if (ses->dhcpv4_request) {
+ if (ses->ses.state == AP_STATE_ACTIVE)
+ dhcpv4_send_reply(DHCPACK, ses->serv->dhcpv4, ses->dhcpv4_request, ses->yiaddr, ses->siaddr, ses->mask, conf_lease_time);
+ else
+ dhcpv4_send_nak(ses->serv->dhcpv4, ses->dhcpv4_request);
+
+ dhcpv4_packet_free(ses->dhcpv4_request);
+ ses->dhcpv4_request = NULL;
+ }
+}
+
+static void ipoe_session_keepalive(struct ipoe_session *ses)
+{
+ if (ses->timer.tpd)
+ triton_timer_mod(&ses->timer, 0);
+
+ ses->xid = ses->dhcpv4_request->hdr->xid;
+
+ if (ses->ses.state == AP_STATE_ACTIVE)
+ dhcpv4_send_reply(DHCPACK, ses->serv->dhcpv4, ses->dhcpv4_request, ses->yiaddr, ses->siaddr, ses->mask, conf_lease_time);
+ else
+ dhcpv4_send_nak(ses->serv->dhcpv4, ses->dhcpv4_request);
+
+ dhcpv4_packet_free(ses->dhcpv4_request);
+ ses->dhcpv4_request = NULL;
+}
+
+static void ipoe_session_started(struct ap_session *s)
+{
+ struct ipoe_session *ses = container_of(s, typeof(*ses), ses);
+
+ log_ppp_debug("ipoe: session started\n");
+
+ ses->timer.expire = ipoe_session_timeout;
+ ses->timer.expire_tv.tv_sec = conf_lease_timeout;
+ if (ses->timer.tpd)
+ triton_timer_mod(&ses->timer, 0);
+}
+
+static void ipoe_session_free(struct ipoe_session *ses)
+{
+ struct unit_cache *uc;
+
+ if (ses->timer.tpd)
+ triton_timer_del(&ses->timer);
+
+ if (ses->dhcpv4_request)
+ dhcpv4_packet_free(ses->dhcpv4_request);
+
+ if (ses->ctrl.called_station_id)
+ _free(ses->ctrl.called_station_id);
+
+ if (ses->ctrl.calling_station_id)
+ _free(ses->ctrl.calling_station_id);
+
+ triton_context_unregister(&ses->ctx);
+
+ if (ses->data)
+ _free(ses->data);
+
+ if (ses->ifindex != -1) {
+ if (uc_size < conf_unit_cache && ipoe_nl_modify(ses->ifindex, 0, 0, "", NULL)) {
+ uc = mempool_alloc(uc_pool);
+ uc->ifindex = ses->ifindex;
+ pthread_mutex_lock(&uc_lock);
+ list_add_tail(&uc->entry, &uc_list);
+ ++uc_size;
+ pthread_mutex_unlock(&uc_lock);
+ } else
+ ipoe_nl_delete(ses->ifindex);
+ }
+
+ mempool_free(ses);
+}
+
+static void ipoe_session_finished(struct ap_session *s)
+{
+ struct ipoe_session *ses = container_of(s, typeof(*ses), ses);
+ int serv_close;
+
+ log_ppp_debug("ipoe: session finished\n");
+
+ pthread_mutex_lock(&ses->serv->lock);
+ list_del(&ses->entry);
+ serv_close = ses->serv->need_close && list_empty(&ses->serv->sessions);
+ pthread_mutex_unlock(&ses->serv->lock);
+
+ if (ses->yiaddr && ses->serv->dhcpv4 && ses->serv->dhcpv4->range)
+ dhcpv4_put_ip(ses->serv->dhcpv4, ses->yiaddr);
+
+ if (serv_close)
+ ipoe_serv_close(&ses->serv->ctx);
+
+ triton_context_call(&ses->ctx, (triton_event_func)ipoe_session_free, ses);
+}
+
+static void ipoe_session_terminate(struct ap_session *s, int hard)
+{
+ ap_session_finished(s);
+}
+
+
+static void ipoe_session_close(struct triton_context_t *ctx)
+{
+ struct ipoe_session *ses = container_of(ctx, typeof(*ses), ctx);
+
+ if (ses->ses.state)
+ ap_session_terminate(&ses->ses, TERM_ADMIN_RESET, 1);
+ else
+ ipoe_session_finished(&ses->ses);
+}
+
+static struct ipoe_session *ipoe_session_create_dhcpv4(struct ipoe_serv *serv, struct dhcpv4_packet *pack)
+{
+ struct ipoe_session *ses;
+ int dlen = 0;
+ uint8_t *ptr;
+
+ ses = mempool_alloc(ses_pool);
+ if (!ses) {
+ log_emerg("out of memery\n");
+ return NULL;
+ }
+
+ memset(ses, 0, sizeof(*ses));
+
+ ap_session_init(&ses->ses);
+
+ ses->serv = serv;
+ ses->ifindex = -1;
+ ses->dhcpv4_request = pack;
+
+ ses->xid = pack->hdr->xid;
+ memcpy(ses->hwaddr, pack->hdr->chaddr, 6);
+ ses->giaddr = pack->hdr->giaddr;
+
+ dhcpv4_get_ip(serv->dhcpv4, &ses->yiaddr, &ses->siaddr, &ses->mask);
+
+ if (pack->agent_circuit_id)
+ dlen += sizeof(struct dhcp_opt) + pack->agent_circuit_id->len;
+
+ if (pack->agent_remote_id)
+ dlen += sizeof(struct dhcp_opt) + pack->agent_remote_id->len;
+
+ if (pack->client_id)
+ dlen += sizeof(struct dhcp_opt) + pack->client_id->len;
+
+ if (dlen) {
+ ses->data = _malloc(dlen);
+ if (!ses->data) {
+ log_emerg("out of memery\n");
+ mempool_free(ses);
+ return NULL;
+ }
+ ptr = ses->data;
+ }
+
+ if (pack->agent_circuit_id) {
+ ses->agent_circuit_id = (struct dhcp_opt *)ptr;
+ ses->agent_circuit_id->len = pack->agent_circuit_id->len;
+ memcpy(ses->agent_circuit_id->data, pack->agent_circuit_id->data, pack->agent_circuit_id->len);
+ ptr += sizeof(struct dhcp_opt) + pack->agent_circuit_id->len;
+ }
+
+ if (pack->agent_remote_id) {
+ ses->agent_remote_id = (struct dhcp_opt *)ptr;
+ ses->agent_remote_id->len = pack->agent_remote_id->len;
+ memcpy(ses->agent_remote_id->data, pack->agent_remote_id->data, pack->agent_remote_id->len);
+ ptr += sizeof(struct dhcp_opt) + pack->agent_remote_id->len;
+ }
+
+ if (pack->client_id) {
+ ses->client_id = (struct dhcp_opt *)ptr;
+ ses->client_id->len = pack->client_id->len;
+ memcpy(ses->client_id->data, pack->client_id->data, pack->client_id->len);
+ ptr += sizeof(struct dhcp_opt) + pack->client_id->len;
+ }
+
+ ses->ctx.before_switch = log_switch;
+ ses->ctx.close = ipoe_session_close;
+ ses->ctrl.ctx = &ses->ctx;
+ ses->ctrl.started = ipoe_session_started;
+ ses->ctrl.finished = ipoe_session_finished;
+ ses->ctrl.terminate = ipoe_session_terminate;
+ ses->ctrl.type = CTRL_TYPE_IPOE;
+ ses->ctrl.name = "ipoe";
+
+ ses->ctrl.calling_station_id = _malloc(19);
+ ses->ctrl.called_station_id = _strdup(serv->ifname);
+
+ ptr = ses->hwaddr;
+ sprintf(ses->ctrl.calling_station_id, "%02x:%02x:%02x:%02x:%02x:%02x",
+ ptr[0], ptr[1], ptr[2], ptr[3], ptr[4], ptr[5]);
+
+ ses->ses.ctrl = &ses->ctrl;
+ ses->ses.chan_name = ses->ctrl.calling_station_id;
+
+ triton_context_register(&ses->ctx, &ses->ses);
+
+ triton_context_wakeup(&ses->ctx);
+
+ //pthread_mutex_lock(&serv->lock);
+ list_add_tail(&ses->entry, &serv->sessions);
+ //pthread_mutex_unlock(&serv->lock);
+
+ triton_context_call(&ses->ctx, (triton_event_func)ipoe_session_start, ses);
+
+ return ses;
+}
+
+static void ipoe_recv_dhcpv4(struct dhcpv4_serv *dhcpv4, struct dhcpv4_packet *pack)
+{
+ struct ipoe_serv *serv = container_of(dhcpv4->ctx, typeof(*serv), ctx);
+ struct ipoe_session *ses;
+ //struct dhcpv4_packet *reply;
+
+ if (ap_shutdown)
+ return;
+
+ pthread_mutex_lock(&serv->lock);
+ if (pack->msg_type == DHCPDISCOVER) {
+ ses = ipoe_session_lookup(serv, pack);
+ if (!ses) {
+ ses = ipoe_session_create_dhcpv4(serv, pack);
+
+ if (conf_verbose && ses) {
+ log_switch(dhcpv4->ctx, &ses->ses);
+ log_ppp_info2("recv ");
+ dhcpv4_print_packet(pack, log_ppp_info2);
+ }
+ } else {
+ log_switch(dhcpv4->ctx, &ses->ses);
+
+ if (conf_verbose) {
+ log_ppp_info2("recv ");
+ dhcpv4_print_packet(pack, log_ppp_info2);
+ }
+
+ if (ses->ses.ipv4 && ses->ses.state == AP_STATE_ACTIVE && pack->request_ip == ses->ses.ipv4->peer_addr)
+ dhcpv4_send_reply(DHCPOFFER, dhcpv4, pack, ses->yiaddr, ses->siaddr, ses->mask, conf_lease_time);
+
+ dhcpv4_packet_free(pack);
+ }
+ } else if (pack->msg_type == DHCPREQUEST) {
+ ses = ipoe_session_lookup(serv, pack);
+
+ if (!ses) {
+ if (conf_verbose) {
+ log_info2("recv ");
+ dhcpv4_print_packet(pack, log_info2);
+ }
+
+ dhcpv4_send_nak(dhcpv4, pack);
+ } else {
+ if ((pack->server_id && (pack->server_id != ses->siaddr || pack->request_ip != ses->yiaddr)) ||
+ (pack->hdr->ciaddr && (pack->hdr->xid != ses->xid || pack->hdr->ciaddr != ses->yiaddr))) {
+
+ if (conf_verbose) {
+ log_info2("recv ");
+ dhcpv4_print_packet(pack, log_info2);
+ }
+
+ if (pack->server_id == ses->siaddr && pack->request_ip && pack->request_ip != ses->yiaddr)
+ dhcpv4_send_nak(dhcpv4, pack);
+
+ ap_session_terminate(&ses->ses, TERM_USER_REQUEST, 0);
+ } else {
+ if (conf_verbose) {
+ log_switch(dhcpv4->ctx, &ses->ses);
+ log_ppp_info2("recv ");
+ dhcpv4_print_packet(pack, log_ppp_info2);
+ }
+
+ if (serv->opt_shared == 0)
+ ipoe_drop_sessions(serv, ses);
+
+ if (ses->ses.state == AP_STATE_STARTING && !ses->dhcpv4_request) {
+ ses->dhcpv4_request = pack;
+ pack = NULL;
+ triton_context_call(&ses->ctx, (triton_event_func)ipoe_session_activate, ses);
+ } else if (ses->ses.state == AP_STATE_ACTIVE && !ses->dhcpv4_request) {
+ ses->dhcpv4_request = pack;
+ pack = NULL;
+ triton_context_call(&ses->ctx, (triton_event_func)ipoe_session_keepalive, ses);
+ }
+ }
+ }
+ if (pack)
+ dhcpv4_packet_free(pack);
+ } else if (pack->msg_type == DHCPDECLINE || pack->msg_type == DHCPRELEASE) {
+ ses = ipoe_session_lookup(serv, pack);
+ if (ses) {
+ if (conf_verbose) {
+ log_switch(dhcpv4->ctx, &ses->ses);
+ log_ppp_info2("recv ");
+ dhcpv4_print_packet(pack, log_ppp_info2);
+ }
+
+ ap_session_terminate(&ses->ses, TERM_USER_REQUEST, 0);
+ }
+ dhcpv4_packet_free(pack);
+ }
+ pthread_mutex_unlock(&serv->lock);
+}
+
+static struct ipoe_session *ipoe_session_create_up(struct ipoe_serv *serv, struct ethhdr *eth, struct iphdr *iph)
+{
+ struct ipoe_session *ses;
+
+ if (ap_shutdown)
+ return NULL;
+
+ ses = mempool_alloc(ses_pool);
+ if (!ses) {
+ log_emerg("out of memery\n");
+ return NULL;
+ }
+
+ memset(ses, 0, sizeof(*ses));
+
+ ap_session_init(&ses->ses);
+
+ ses->serv = serv;
+ ses->ifindex = -1;
+
+ memcpy(ses->hwaddr, eth->h_source, 6);
+
+ ses->ctx.before_switch = log_switch;
+ ses->ctx.close = ipoe_session_close;
+ ses->ctrl.ctx = &ses->ctx;
+ ses->ctrl.started = ipoe_session_started;
+ ses->ctrl.finished = ipoe_session_finished;
+ ses->ctrl.terminate = ipoe_session_terminate;
+ ses->ctrl.type = CTRL_TYPE_IPOE;
+ ses->ctrl.name = "ipoe";
+
+ ses->yiaddr = iph->saddr;
+
+ ses->ctrl.calling_station_id = _malloc(17);
+ ses->ctrl.called_station_id = _malloc(17);
+
+ u_inet_ntoa(iph->saddr, ses->ctrl.calling_station_id);
+ u_inet_ntoa(iph->daddr, ses->ctrl.called_station_id);
+
+ ses->ses.username = _strdup(ses->ctrl.calling_station_id);
+
+ ses->ses.ctrl = &ses->ctrl;
+ ses->ses.chan_name = ses->ctrl.calling_station_id;
+
+ triton_context_register(&ses->ctx, &ses->ses);
+
+ triton_context_wakeup(&ses->ctx);
+
+ //pthread_mutex_lock(&serv->lock);
+ list_add_tail(&ses->entry, &serv->sessions);
+ //pthread_mutex_unlock(&serv->lock);
+
+ triton_context_call(&ses->ctx, (triton_event_func)ipoe_session_start, ses);
+
+ return ses;
+}
+
+struct ipoe_session *ipoe_session_alloc(void)
+{
+ struct ipoe_session *ses;
+
+ ses = mempool_alloc(ses_pool);
+ if (!ses) {
+ log_emerg("out of memery\n");
+ return NULL;
+ }
+
+ memset(ses, 0, sizeof(*ses));
+
+ ap_session_init(&ses->ses);
+
+ ses->ifindex = -1;
+
+ ses->ctx.before_switch = log_switch;
+ ses->ctx.close = ipoe_session_close;
+ ses->ctrl.ctx = &ses->ctx;
+ ses->ctrl.started = ipoe_session_started;
+ ses->ctrl.finished = ipoe_session_finished;
+ ses->ctrl.terminate = ipoe_session_terminate;
+ ses->ctrl.type = CTRL_TYPE_IPOE;
+ ses->ctrl.name = "ipoe";
+
+ ses->ses.ctrl = &ses->ctrl;
+ ses->ses.chan_name = ses->ctrl.calling_station_id;
+
+ return ses;
+}
+
+void ipoe_recv_up(int ifindex, struct ethhdr *eth, struct iphdr *iph)
+{
+ struct ipoe_serv *serv;
+ struct ipoe_session *ses;
+
+ list_for_each_entry(serv, &serv_list, entry) {
+ if (serv->ifindex != ifindex)
+ continue;
+
+ if (!serv->opt_up)
+ return;
+
+ pthread_mutex_lock(&serv->lock);
+ list_for_each_entry(ses, &serv->sessions, entry) {
+ if (ses->yiaddr == iph->saddr) {
+ pthread_mutex_unlock(&serv->lock);
+ return;
+ }
+ }
+ pthread_mutex_unlock(&serv->lock);
+
+ ipoe_session_create_up(serv, eth, iph);
+
+ break;
+ }
+}
+
+static void ipoe_serv_close(struct triton_context_t *ctx)
+{
+ struct ipoe_serv *serv = container_of(ctx, typeof(*serv), ctx);
+
+ pthread_mutex_lock(&serv->lock);
+ if (!list_empty(&serv->sessions)) {
+ serv->need_close = 1;
+ pthread_mutex_unlock(&serv->lock);
+ return;
+ }
+ pthread_mutex_unlock(&serv->lock);
+
+ if (serv->dhcpv4)
+ dhcpv4_free(serv->dhcpv4);
+
+ triton_context_unregister(ctx);
+
+ _free(serv->ifname);
+ _free(serv);
+}
+
+static int show_stat_exec(const char *cmd, char * const *fields, int fields_cnt, void *client)
+{
+ cli_send(client, "ipoe:\r\n");
+ cli_sendv(client," starting: %u\r\n", stat_starting);
+ cli_sendv(client," active: %u\r\n", stat_active);
+
+ return CLI_CMD_OK;
+}
+
+void __export ipoe_get_stat(unsigned int **starting, unsigned int **active)
+{
+ *starting = &stat_starting;
+ *active = &stat_active;
+}
+
+static void __terminate(struct ap_session *ses)
+{
+ ap_session_terminate(ses, TERM_NAS_REQUEST, 0);
+}
+
+static void ipoe_drop_sessions(struct ipoe_serv *serv, struct ipoe_session *skip)
+{
+ struct ipoe_session *ses;
+
+ list_for_each_entry(ses, &serv->sessions, entry) {
+ if (ses == skip)
+ continue;
+
+ if (ses->ses.state == AP_STATE_ACTIVE)
+ ap_session_ifdown(&ses->ses);
+
+ triton_context_call(&ses->ctx, (triton_event_func)__terminate, &ses->ses);
+ }
+}
+
+struct ipoe_serv *ipoe_find_serv(const char *ifname)
+{
+ struct ipoe_serv *serv;
+
+ list_for_each_entry(serv, &serv_list, entry) {
+ if (strcmp(serv->ifname, ifname) == 0)
+ return serv;
+ }
+
+ return NULL;
+}
+
+static void add_interface(const char *ifname, int ifindex, const char *opt)
+{
+ char *str0, *str, *ptr1, *ptr2;
+ int end;
+ struct ipoe_serv *serv;
+ int opt_shared = conf_shared;
+ int opt_dhcpv4 = 0;
+ int opt_up = 0;
+ int opt_mode = conf_mode;
+
+ str0 = strchr(opt, ',');
+ if (str0) {
+ str0 = _strdup(str0 + 1);
+ str = str0;
+
+ while (1) {
+ for (ptr1 = str + 1; *ptr1 && *ptr1 != '='; ptr1++);
+
+ if (!*ptr1)
+ goto parse_err;
+
+ *ptr1 = 0;
+
+ for (ptr2 = ++ptr1; *ptr2 && *ptr2 != ','; ptr2++);
+
+ end = *ptr2 == 0;
+
+ if (!end)
+ *ptr2 = 0;
+
+ if (ptr2 == ptr1)
+ goto parse_err;
+
+ if (strcmp(str, "start") == 0) {
+ if (!strcmp(ptr1, "up"))
+ opt_up = 1;
+ else if (!strcmp(ptr1, "dhcpv4"))
+ opt_dhcpv4 = 1;
+ else
+ goto parse_err;
+ } else if (strcmp(str, "shared") == 0) {
+ opt_shared = atoi(ptr1);
+ } else if (strcmp(str, "mode") == 0) {
+ if (!strcmp(ptr1, "L2"))
+ opt_mode = MODE_L2;
+ else if (!strcmp(ptr1, "L3"))
+ opt_mode = MODE_L3;
+ else
+ goto parse_err;
+ }
+
+ if (end)
+ break;
+
+ str = ptr2 + 1;
+ }
+
+ _free(str0);
+ }
+
+ if (!opt_up && !opt_dhcpv4) {
+ opt_up = conf_up;
+ opt_dhcpv4 = conf_dhcpv4;
+ }
+
+ list_for_each_entry(serv, &serv_list, entry) {
+ if (strcmp(ifname, serv->ifname))
+ continue;
+
+ serv->active = 1;
+ serv->ifindex = ifindex;
+
+ if ((opt_shared && !serv->opt_shared) || (!opt_shared && serv->opt_shared)) {
+ ipoe_drop_sessions(serv, NULL);
+ serv->opt_shared = opt_shared;
+ }
+
+ if (opt_dhcpv4 && !serv->dhcpv4) {
+ serv->dhcpv4 = dhcpv4_create(&serv->ctx, serv->ifname, opt);
+ if (serv->dhcpv4)
+ serv->dhcpv4->recv = ipoe_recv_dhcpv4;
+ } else if (!opt_dhcpv4 && serv->dhcpv4) {
+ dhcpv4_free(serv->dhcpv4);
+ serv->dhcpv4 = NULL;
+ }
+
+ serv->opt_up = opt_up;
+ serv->opt_mode = conf_mode;
+
+ return;
+ }
+
+ serv = _malloc(sizeof(*serv));
+ memset(serv, 0, sizeof(*serv));
+ serv->ctx.close = ipoe_serv_close;
+ serv->ifname = _strdup(ifname);
+ serv->ifindex = ifindex;
+ serv->opt_shared = opt_shared;
+ serv->opt_dhcpv4 = opt_dhcpv4;
+ serv->opt_up = opt_up;
+ serv->opt_mode = opt_mode;
+ serv->active = 1;
+ INIT_LIST_HEAD(&serv->sessions);
+ pthread_mutex_init(&serv->lock, NULL);
+
+ triton_context_register(&serv->ctx, NULL);
+
+ if (serv->opt_dhcpv4) {
+ serv->dhcpv4 = dhcpv4_create(&serv->ctx, serv->ifname, opt);
+ if (serv->dhcpv4)
+ serv->dhcpv4->recv = ipoe_recv_dhcpv4;
+ }
+
+ triton_context_wakeup(&serv->ctx);
+
+ list_add_tail(&serv->entry, &serv_list);
+
+ return;
+
+parse_err:
+ log_error("ipoe: failed to parse '%s'\n", opt);
+ _free(str0);
+}
+
+static void load_interface(const char *opt)
+{
+ const char *ptr;
+ struct ifreq ifr;
+
+ for (ptr = opt; *ptr && *ptr != ','; ptr++);
+
+ if (ptr - opt >= sizeof(ifr.ifr_name))
+ return;
+
+ memcpy(ifr.ifr_name, opt, ptr - opt);
+ ifr.ifr_name[ptr - opt] = 0;
+
+ if (ioctl(sock_fd, SIOCGIFINDEX, &ifr)) {
+ log_error("ipoe: '%s': ioctl(SIOCGIFINDEX): %s\n", ifr.ifr_name, strerror(errno));
+ return;
+ }
+
+ add_interface(ifr.ifr_name, ifr.ifr_ifindex, opt);
+}
+
+static int __load_interface_re(int index, int flags, const char *name, struct iplink_arg *arg)
+{
+ if (pcre_exec(arg->re, NULL, name, strlen(name), 0, 0, NULL, 0) < 0)
+ return 0;
+
+ add_interface(name, index, arg->opt);
+
+ return 0;
+}
+
+static void load_interface_re(const char *opt)
+{
+ pcre *re = NULL;
+ const char *pcre_err;
+ char *pattern;
+ const char *ptr;
+ int pcre_offset;
+ struct iplink_arg arg;
+
+ for (ptr = opt; *ptr && *ptr != ','; ptr++);
+
+ pattern = _malloc(ptr - (opt + 3) + 1);
+ memcpy(pattern, opt + 3, ptr - (opt + 3));
+ pattern[ptr - (opt + 3)] = 0;
+
+ re = pcre_compile2(pattern, 0, NULL, &pcre_err, &pcre_offset, NULL);
+
+ if (!re) {
+ log_error("ipoe: %s at %i\r\n", pcre_err, pcre_offset);
+ return;
+ }
+
+ arg.re = re;
+ arg.opt = opt;
+
+ iplink_list((iplink_list_func)__load_interface_re, &arg);
+
+ pcre_free(re);
+ _free(pattern);
+}
+
+static void load_interfaces(struct conf_sect_t *sect)
+{
+ struct ipoe_serv *serv;
+ struct conf_option_t *opt;
+ struct list_head *pos, *n;
+
+ list_for_each_entry(serv, &serv_list, entry)
+ serv->active = 0;
+
+ list_for_each_entry(opt, &sect->items, entry) {
+ if (strcmp(opt->name, "interface"))
+ continue;
+ if (!opt->val)
+ continue;
+
+ if (strlen(opt->val) > 3 && memcmp(opt->val, "re:", 3) == 0)
+ load_interface_re(opt->val);
+ else
+ load_interface(opt->val);
+ }
+
+ list_for_each_safe(pos, n, &serv_list) {
+ serv = list_entry(pos, typeof(*serv), entry);
+ if (!serv->active) {
+ ipoe_drop_sessions(serv, NULL);
+ list_del(&serv->entry);
+ triton_context_call(&serv->ctx, (triton_event_func)ipoe_serv_close, &serv->ctx);
+ }
+ }
+}
+
+static void parse_local_net(const char *opt)
+{
+ const char *ptr;
+ char str[17];
+ in_addr_t addr;
+ int mask;
+ char *endptr;
+
+ ptr = strchr(opt, '/');
+ if (ptr) {
+ memcpy(str, opt, ptr - opt);
+ str[ptr - opt] = 0;
+ addr = inet_addr(str);
+ if (addr == INADDR_NONE)
+ goto out_err;
+ mask = strtoul(ptr + 1, &endptr, 10);
+ if (mask > 32)
+ goto out_err;
+ } else {
+ addr = inet_addr(opt);
+ if (addr == INADDR_NONE)
+ goto out_err;
+ mask = 24;
+ }
+
+ mask = (1 << mask) - 1;
+
+ ipoe_nl_add_net(addr & mask, mask);
+
+ return;
+
+out_err:
+ log_error("ipoe: failed to parse 'local-net=%s'\n", opt);
+}
+
+static void load_local_nets(struct conf_sect_t *sect)
+{
+ struct conf_option_t *opt;
+
+ ipoe_nl_delete_nets();
+
+ list_for_each_entry(opt, &sect->items, entry) {
+ if (strcmp(opt->name, "local-net"))
+ continue;
+ if (!opt->val)
+ continue;
+ parse_local_net(opt->val);
+ }
+}
+
+static void load_config(void)
+{
+ const char *opt;
+ struct conf_sect_t *s = conf_get_section("ipoe");
+ struct conf_option_t *opt1;
+
+ if (!s)
+ return;
+
+ opt = conf_get_opt("ipoe", "username");
+ if (opt) {
+ if (strcmp(opt, "ifname") == 0)
+ conf_username = USERNAME_IFNAME;
+#ifdef USE_LUA
+ else if (strlen(opt) > 4 && memcmp(opt, "lua:", 4) == 0) {
+ conf_username = USERNAME_LUA;
+ conf_lua_username_func = opt + 4;
+ }
+#endif
+ else
+ log_emerg("ipoe: unknown username value '%s'\n", opt);
+ }
+
+ opt = conf_get_opt("ipoe", "gw-ip-address");
+ if (opt)
+ conf_gw_address = inet_addr(opt);
+ else
+ conf_gw_address = 0;
+
+ opt = conf_get_opt("ipoe", "netmask");
+ if (opt) {
+ conf_netmask = atoi(opt);
+ if (conf_netmask <= 0 || conf_netmask > 32) {
+ log_error("ipoe: invalid netmask %s\n", opt);
+ conf_netmask = 0;
+ }
+ } else
+ conf_netmask = 0;
+
+ opt = conf_get_opt("ipoe", "verbose");
+ if (opt)
+ conf_verbose = atoi(opt);
+
+ opt = conf_get_opt("ipoe", "lease-time");
+ if (opt)
+ conf_lease_time = atoi(opt);
+
+ opt = conf_get_opt("ipoe", "lease-timeout");
+ if (opt)
+ conf_lease_timeout = atoi(opt);
+
+ opt = conf_get_opt("ipoe", "unit-cache");
+ if (opt)
+ conf_unit_cache = atoi(opt);
+
+ opt = conf_get_opt("ipoe", "shared");
+ if (opt)
+ conf_shared = atoi(opt);
+ else
+ conf_shared = 1;
+
+ opt = conf_get_opt("ipoe", "mode");
+ if (opt) {
+ if (!strcmp(opt, "L2"))
+ conf_mode = MODE_L2;
+ else if (!strcmp(opt, "L3"))
+ conf_mode = MODE_L3;
+ else
+ log_emerg("ipoe: failed to parse 'mode=%s'\n", opt);
+ } else
+ conf_mode = MODE_L2;
+
+ conf_dhcpv4 = 0;
+ conf_up = 0;
+
+ list_for_each_entry(opt1, &s->items, entry) {
+ if (strcmp(opt1->name, "start"))
+ continue;
+ if (!strcmp(opt1->val, "dhcpv4"))
+ conf_dhcpv4 = 1;
+ else if (!strcmp(opt1->val, "up"))
+ conf_up = 1;
+ }
+
+ if (!conf_dhcpv4 && !conf_up)
+ conf_dhcpv4 = 1;
+
+ load_interfaces(s);
+ load_local_nets(s);
+}
+
+static void ipoe_init(void)
+{
+ ses_pool = mempool_create(sizeof(struct ipoe_session));
+ uc_pool = mempool_create(sizeof(struct unit_cache));
+
+ load_config();
+
+ cli_register_simple_cmd2(show_stat_exec, NULL, 2, "show", "stat");
+
+ triton_event_register_handler(EV_CONFIG_RELOAD, (triton_event_func)load_config);
+}
+
+DEFINE_INIT(20, ipoe_init);
diff --git a/accel-pppd/ctrl/ipoe/ipoe.h b/accel-pppd/ctrl/ipoe/ipoe.h
new file mode 100644
index 0000000..d18db69
--- /dev/null
+++ b/accel-pppd/ctrl/ipoe/ipoe.h
@@ -0,0 +1,84 @@
+#ifndef __IPOE_H
+#define __IPOE_H
+
+#include <stdint.h>
+#include <pthread.h>
+
+#include "triton.h"
+#include "ap_session.h"
+#include "dhcpv4.h"
+
+struct ipoe_serv
+{
+ struct list_head entry;
+ struct triton_context_t ctx;
+ char *ifname;
+ int ifindex;
+ int active;
+ struct list_head sessions;
+ struct dhcpv4_serv *dhcpv4;
+ pthread_mutex_t lock;
+ int opt_mode;
+ int opt_shared:1;
+ int opt_dhcpv4:1;
+ int opt_up:1;
+ int need_close:1;
+};
+
+struct dhcp_opt
+{
+ uint8_t len;
+ uint8_t data[0];
+};
+
+struct ipoe_session
+{
+ struct list_head entry;
+ struct triton_context_t ctx;
+ struct triton_timer_t timer;
+ struct ipoe_serv *serv;
+ struct ap_ctrl ctrl;
+ struct ap_session ses;
+ uint8_t hwaddr[6];
+ struct dhcp_opt *client_id;
+ struct dhcp_opt *agent_circuit_id;
+ struct dhcp_opt *agent_remote_id;
+ uint32_t xid;
+ uint32_t giaddr;
+ uint32_t yiaddr;
+ uint32_t siaddr;
+ int mask;
+ uint8_t *data;
+ struct dhcpv4_packet *dhcpv4_request;
+ int ifindex;
+};
+
+struct ipoe_session_info
+{
+ struct list_head entry;
+ int ifindex;
+ uint32_t addr;
+ uint32_t peer_addr;
+};
+
+#ifdef USE_LUA
+int ipoe_lua_set_username(struct ipoe_session *, const char *func);
+#endif
+
+struct iphdr;
+struct ethhdr;
+
+void ipoe_recv_up(int ifindex, struct ethhdr *eth, struct iphdr *iph);
+struct ipoe_session *ipoe_session_alloc(void);
+
+struct ipoe_serv *ipoe_find_serv(const char *ifname);
+
+void ipoe_nl_add_net(uint32_t addr, int mask);
+void ipoe_nl_delete_nets(void);
+int ipoe_nl_create(uint32_t peer_addr, uint32_t addr, const char *ifname, uint8_t *hwaddr);
+void ipoe_nl_delete(int ifindex);
+int ipoe_nl_modify(int ifindex, uint32_t peer_addr, uint32_t addr, const char *ifname, uint8_t *hwaddr);
+void ipoe_nl_get_sessions(struct list_head *list);
+
+#endif
+
diff --git a/accel-pppd/ctrl/ipoe/ipoe_netlink.c b/accel-pppd/ctrl/ipoe/ipoe_netlink.c
new file mode 100644
index 0000000..b92ec92
--- /dev/null
+++ b/accel-pppd/ctrl/ipoe/ipoe_netlink.c
@@ -0,0 +1,480 @@
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdio.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 <netinet/ip.h>
+#include <arpa/inet.h>
+#include <linux/if.h>
+#include <linux/genetlink.h>
+
+#include "triton.h"
+#include "log.h"
+#include "genl.h"
+#include "libnetlink.h"
+
+#include "ipoe.h"
+#include "if_ipoe.h"
+
+#include "memdebug.h"
+
+#define PKT_ATTR_MAX 256
+
+static struct rtnl_handle rth;
+static struct triton_md_handler_t up_hnd;
+static int ipoe_genl_id;
+
+void ipoe_nl_delete_nets(void)
+{
+ struct nlmsghdr *nlh;
+ struct genlmsghdr *ghdr;
+ struct {
+ struct nlmsghdr n;
+ char buf[1024];
+ } req;
+
+ if (rth.fd == -1)
+ return;
+
+ nlh = &req.n;
+ nlh->nlmsg_len = NLMSG_LENGTH(GENL_HDRLEN);
+ nlh->nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK;
+ nlh->nlmsg_type = ipoe_genl_id;
+
+ ghdr = NLMSG_DATA(&req.n);
+ ghdr->cmd = IPOE_CMD_DEL_NET;
+
+ addattr32(nlh, 1024, IPOE_ATTR_ADDR, 0);
+
+ if (rtnl_talk(&rth, nlh, 0, 0, nlh, NULL, NULL, 0) < 0 )
+ log_error("ipoe: nl_del_net: error talking to kernel\n");
+}
+
+void ipoe_nl_add_net(uint32_t addr, int mask)
+{
+ struct nlmsghdr *nlh;
+ struct genlmsghdr *ghdr;
+ struct {
+ struct nlmsghdr n;
+ char buf[1024];
+ } req;
+
+ if (rth.fd == -1)
+ return;
+
+ nlh = &req.n;
+ nlh->nlmsg_len = NLMSG_LENGTH(GENL_HDRLEN);
+ nlh->nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK;
+ nlh->nlmsg_type = ipoe_genl_id;
+
+ ghdr = NLMSG_DATA(&req.n);
+ ghdr->cmd = IPOE_CMD_ADD_NET;
+
+ addattr32(nlh, 1024, IPOE_ATTR_ADDR, addr);
+ addattr32(nlh, 1024, IPOE_ATTR_MASK, mask);
+
+ if (rtnl_talk(&rth, nlh, 0, 0, nlh, NULL, NULL, 0) < 0 )
+ log_error("ipoe: nl_add_net: error talking to kernel\n");
+}
+
+int ipoe_nl_create(uint32_t peer_addr, uint32_t addr, const char *ifname, uint8_t *hwaddr)
+{
+ struct rtnl_handle rth;
+ struct nlmsghdr *nlh;
+ struct genlmsghdr *ghdr;
+ struct rtattr *tb[IPOE_ATTR_MAX + 1];
+ struct rtattr *attrs;
+ int len;
+ int ret = -1;
+ struct {
+ struct nlmsghdr n;
+ char buf[1024];
+ } req;
+ union {
+ uint8_t hwaddr[6];
+ uint64_t u64;
+ } u;
+
+ if (rtnl_open_byproto(&rth, 0, NETLINK_GENERIC)) {
+ log_ppp_error("ipoe: cannot open generic netlink socket\n");
+ return -1;
+ }
+
+ nlh = &req.n;
+ nlh->nlmsg_len = NLMSG_LENGTH(GENL_HDRLEN);
+ nlh->nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK;
+ nlh->nlmsg_type = ipoe_genl_id;
+
+ ghdr = NLMSG_DATA(&req.n);
+ ghdr->cmd = IPOE_CMD_CREATE;
+
+ if (peer_addr)
+ addattr32(nlh, 1024, IPOE_ATTR_PEER_ADDR, peer_addr);
+
+ if (addr)
+ addattr32(nlh, 1024, IPOE_ATTR_ADDR, addr);
+
+ if (hwaddr) {
+ memcpy(u.hwaddr, hwaddr, 6);
+ addattr_l(nlh, 1024, IPOE_ATTR_HWADDR, &u.u64, 8);
+ }
+
+ if (ifname)
+ addattr_l(nlh, 1024, IPOE_ATTR_IFNAME, ifname, strlen(ifname) + 1);
+
+ if (rtnl_talk(&rth, nlh, 0, 0, nlh, NULL, NULL, 0) < 0 )
+ log_ppp_error("ipoe: nl_create: error talking to kernel\n");
+
+ if (nlh->nlmsg_type != ipoe_genl_id) {
+ log_ppp_error("ipoe: not a IPoE message %d\n", nlh->nlmsg_type);
+ goto out;
+ }
+
+ ghdr = NLMSG_DATA(nlh);
+
+ if (ghdr->cmd != IPOE_CMD_CREATE) {
+ log_ppp_error("ipoe: unknown IPoE command %d\n", ghdr->cmd);
+ goto out;
+ }
+
+ len = nlh->nlmsg_len - NLMSG_LENGTH(GENL_HDRLEN);
+
+ if (len < 0) {
+ log_ppp_error("ipoe: wrong IPoE message len %d\n", len);
+ goto out;
+ }
+
+ attrs = (struct rtattr *)((char *)ghdr + GENL_HDRLEN);
+ parse_rtattr(tb, IPOE_ATTR_MAX, attrs, len);
+
+ if (!tb[IPOE_ATTR_IFINDEX]) {
+ log_ppp_error("ipoe: missing IPOE_ATTR_IFINDEX attribute\n");
+ goto out;
+ }
+
+ ret = *(uint32_t *)(RTA_DATA(tb[IPOE_ATTR_IFINDEX]));
+
+out:
+ rtnl_close(&rth);
+
+ return ret;
+}
+
+int ipoe_nl_modify(int ifindex, uint32_t peer_addr, uint32_t addr, const char *ifname, uint8_t *hwaddr)
+{
+ struct rtnl_handle rth;
+ struct nlmsghdr *nlh;
+ struct genlmsghdr *ghdr;
+ int ret = 0;
+ struct {
+ struct nlmsghdr n;
+ char buf[1024];
+ } req;
+ union {
+ uint8_t hwaddr[6];
+ uint64_t u64;
+ } u;
+
+ if (rtnl_open_byproto(&rth, 0, NETLINK_GENERIC)) {
+ log_ppp_error("ipoe: cannot open generic netlink socket\n");
+ return -1;
+ }
+
+ nlh = &req.n;
+ nlh->nlmsg_len = NLMSG_LENGTH(GENL_HDRLEN);
+ nlh->nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK;
+ nlh->nlmsg_type = ipoe_genl_id;
+
+ ghdr = NLMSG_DATA(&req.n);
+ ghdr->cmd = IPOE_CMD_MODIFY;
+
+ addattr32(nlh, 1024, IPOE_ATTR_IFINDEX, ifindex);
+ addattr32(nlh, 1024, IPOE_ATTR_PEER_ADDR, peer_addr);
+ addattr32(nlh, 1024, IPOE_ATTR_ADDR, addr);
+
+ if (hwaddr) {
+ memcpy(u.hwaddr, hwaddr, 6);
+ addattr_l(nlh, 1024, IPOE_ATTR_HWADDR, &u.u64, 8);
+ }
+
+ if (ifname)
+ addattr_l(nlh, 1024, IPOE_ATTR_IFNAME, ifname, strlen(ifname) + 1);
+
+ if (rtnl_talk(&rth, nlh, 0, 0, nlh, NULL, NULL, 0) < 0 ) {
+ log_ppp_error("ipoe: nl_create: error talking to kernel\n");
+ ret = -1;
+ }
+
+ rtnl_close(&rth);
+
+ return ret;
+}
+
+static int dump_session(const struct sockaddr_nl *addr, struct nlmsghdr *n, void *arg)
+{
+ struct list_head *list = arg;
+ struct ipoe_session_info *info;
+ struct rtattr *tb[IPOE_ATTR_MAX + 1];
+ struct genlmsghdr *ghdr = NLMSG_DATA(n);
+ int len = n->nlmsg_len;
+ struct rtattr *attrs;
+
+ if (ghdr->cmd != IPOE_CMD_GET) {
+ log_error("ipoe: dump_session: got unexpected command %d\n", ghdr->cmd);
+ return 0;
+ }
+
+ len -= NLMSG_LENGTH(GENL_HDRLEN);
+ if (len < 0 ) {
+ log_error("ipoe: dump_session: wrong message length %i\n", len);
+ return -1;
+ }
+
+ attrs = (struct rtattr *)((char *)ghdr + GENL_HDRLEN);
+ parse_rtattr(tb, IPOE_ATTR_MAX, attrs, len);
+
+ info = _malloc(sizeof(*info));
+ if (!info) {
+ log_emerg("out of memory\n");
+ return -1;
+ }
+
+ memset(info, 0, sizeof(*info));
+
+ if (tb[IPOE_ATTR_IFINDEX])
+ info->ifindex = *(uint32_t *)(RTA_DATA(tb[IPOE_ATTR_IFINDEX]));
+ else {
+ log_error("ipoe: dump_session: IPOE_ATTR_IFINDEX is absent\n");
+ _free(info);
+ return 0;
+ }
+
+ if (tb[IPOE_ATTR_ADDR])
+ info->addr = *(uint32_t *)(RTA_DATA(tb[IPOE_ATTR_ADDR]));
+
+ if (tb[IPOE_ATTR_PEER_ADDR])
+ info->peer_addr = *(uint32_t *)(RTA_DATA(tb[IPOE_ATTR_PEER_ADDR]));
+
+ list_add_tail(&info->entry, list);
+
+ return 0;
+}
+
+void ipoe_nl_get_sessions(struct list_head *list)
+{
+ struct nlmsghdr *nlh;
+ struct genlmsghdr *ghdr;
+ struct {
+ struct nlmsghdr n;
+ char buf[1024];
+ } req;
+
+ if (rth.fd == -1)
+ return;
+
+ nlh = &req.n;
+ nlh->nlmsg_len = NLMSG_LENGTH(GENL_HDRLEN);
+ nlh->nlmsg_flags = NLM_F_ROOT | NLM_F_MATCH | NLM_F_REQUEST;
+ nlh->nlmsg_type = ipoe_genl_id;
+ nlh->nlmsg_seq = rth.dump = ++rth.seq;
+
+ ghdr = NLMSG_DATA(&req.n);
+ ghdr->cmd = IPOE_CMD_GET;
+
+ if (rtnl_send(&rth, (char *)nlh, nlh->nlmsg_len) < 0) {
+ log_emerg("ipoe: failed to send dump request: %s\n", strerror(errno));
+ return;
+ }
+
+ rtnl_dump_filter(&rth, dump_session, list, NULL, NULL);
+}
+
+void ipoe_nl_delete(int ifindex)
+{
+ struct rtnl_handle rth;
+ struct nlmsghdr *nlh;
+ struct genlmsghdr *ghdr;
+ struct {
+ struct nlmsghdr n;
+ char buf[1024];
+ } req;
+
+ if (rtnl_open_byproto(&rth, 0, NETLINK_GENERIC)) {
+ log_ppp_error("ipoe: cannot open generic netlink socket\n");
+ return;
+ }
+
+ nlh = &req.n;
+ nlh->nlmsg_len = NLMSG_LENGTH(GENL_HDRLEN);
+ nlh->nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK;
+ nlh->nlmsg_type = ipoe_genl_id;
+
+ ghdr = NLMSG_DATA(&req.n);
+ ghdr->cmd = IPOE_CMD_DELETE;
+
+ addattr32(nlh, 128, IPOE_ATTR_IFINDEX, ifindex);
+
+ if (rtnl_talk(&rth, nlh, 0, 0, nlh, NULL, NULL, 0) < 0 )
+ log_ppp_error("ipoe: nl_delete: error talking to kernel\n");
+
+ rtnl_close(&rth);
+}
+
+static void ipoe_up_handler(const struct sockaddr_nl *addr, struct nlmsghdr *h)
+{
+ struct rtattr *tb[PKT_ATTR_MAX + 1];
+ struct rtattr *tb2[IPOE_ATTR_MAX + 1];
+ struct genlmsghdr *ghdr = NLMSG_DATA(h);
+ int len = h->nlmsg_len;
+ struct rtattr *attrs;
+ int i;
+ int ifindex;
+ struct iphdr *iph;
+ struct ethhdr *eth;
+
+ if (ghdr->cmd != IPOE_REP_PKT)
+ return;
+
+ len -= NLMSG_LENGTH(GENL_HDRLEN);
+
+ if (len < 0) {
+ log_warn("ipoe: wrong controller message length %d\n", len);
+ return;
+ }
+
+ attrs = (struct rtattr *)((char *)ghdr + GENL_HDRLEN);
+ parse_rtattr(tb, PKT_ATTR_MAX, attrs, len);
+
+ for (i = 1; i < PKT_ATTR_MAX; i++) {
+ if (!tb[i])
+ break;
+
+ parse_rtattr_nested(tb2, IPOE_ATTR_MAX, tb[i]);
+
+ if (!tb2[IPOE_ATTR_ETH_HDR] || !tb2[IPOE_ATTR_IP_HDR] || !tb2[IPOE_ATTR_IFINDEX])
+ continue;
+
+ ifindex = *(uint32_t *)(RTA_DATA(tb2[IPOE_ATTR_IFINDEX]));
+ iph = (struct iphdr *)(RTA_DATA(tb2[IPOE_ATTR_IP_HDR]));
+ eth = (struct ethhdr *)(RTA_DATA(tb2[IPOE_ATTR_ETH_HDR]));
+
+ ipoe_recv_up(ifindex, eth, iph);
+ }
+}
+
+static int ipoe_up_read(struct triton_md_handler_t *h)
+{
+ int status;
+ struct nlmsghdr *hdr;
+ struct sockaddr_nl nladdr;
+ struct iovec iov;
+ struct msghdr msg = {
+ .msg_name = &nladdr,
+ .msg_namelen = sizeof(nladdr),
+ .msg_iov = &iov,
+ .msg_iovlen = 1,
+ };
+ char buf[8192];
+
+ memset(&nladdr, 0, sizeof(nladdr));
+ nladdr.nl_family = AF_NETLINK;
+ nladdr.nl_pid = 0;
+ nladdr.nl_groups = 0;
+
+ iov.iov_base = buf;
+ while (1) {
+ iov.iov_len = sizeof(buf);
+ status = recvmsg(h->fd, &msg, 0);
+
+ if (status < 0) {
+ if (errno == EAGAIN)
+ break;
+ log_error("ipoe: netlink error: %s\n", strerror(errno));
+ if (errno == ENOBUFS)
+ continue;
+ return 0;
+ }
+ if (status == 0) {
+ log_error("ipoe: EOF on netlink\n");
+ return 0;
+ }
+ if (msg.msg_namelen != sizeof(nladdr)) {
+ log_error("ipoe: netlink sender address length == %d\n", msg.msg_namelen);
+ return 0;
+ }
+ for (hdr = (struct nlmsghdr*)buf; status >= sizeof(*hdr); ) {
+ int len = hdr->nlmsg_len;
+ int l = len - sizeof(*h);
+
+ if (l<0 || len>status) {
+ if (msg.msg_flags & MSG_TRUNC) {
+ log_warn("ipoe: truncated netlink message\n");
+ continue;
+ }
+ log_error("ipoe: malformed netlink message\n");
+ continue;
+ }
+
+ ipoe_up_handler(&nladdr, hdr);
+
+ status -= NLMSG_ALIGN(len);
+ hdr = (struct nlmsghdr*)((char*)hdr + NLMSG_ALIGN(len));
+ }
+ if (msg.msg_flags & MSG_TRUNC) {
+ log_warn("ipoe: netlink message truncated\n");
+ continue;
+ }
+ if (status) {
+ log_error("ipoe: netlink remnant of size %d\n", status);
+ return 0;
+ }
+ }
+
+ return 0;
+}
+
+static void ipoe_up_close(struct triton_context_t *ctx)
+{
+ triton_md_unregister_handler(&up_hnd);
+ triton_context_unregister(ctx);
+}
+
+static struct triton_context_t up_ctx = {
+ .close = ipoe_up_close,
+};
+
+static struct triton_md_handler_t up_hnd = {
+ .read = ipoe_up_read,
+};
+
+static void init(void)
+{
+ int mcg_id = genl_resolve_mcg(IPOE_GENL_NAME, IPOE_GENL_MCG_PKT, &ipoe_genl_id);
+ if (mcg_id == -1) {
+ log_warn("ipoe: unclassified packet handling is disabled\n");
+ rth.fd = -1;
+ return;
+ }
+
+ if (rtnl_open_byproto(&rth, 1 << (mcg_id - 1), NETLINK_GENERIC)) {
+ log_error("ipoe: cannot open generic netlink socket\n");
+ rth.fd = -1;
+ return;
+ }
+
+ fcntl(rth.fd, F_SETFL, O_NONBLOCK);
+ fcntl(rth.fd, F_SETFD, fcntl(rth.fd, F_GETFD) | FD_CLOEXEC);
+
+ triton_context_register(&up_ctx, NULL);
+ up_hnd.fd = rth.fd;
+ triton_md_register_handler(&up_ctx, &up_hnd);
+ triton_md_enable_handler(&up_hnd, MD_MODE_READ);
+ triton_context_wakeup(&up_ctx);
+}
+
+DEFINE_INIT(19, init);
diff --git a/accel-pppd/ctrl/ipoe/lua.c b/accel-pppd/ctrl/ipoe/lua.c
new file mode 100644
index 0000000..0b24635
--- /dev/null
+++ b/accel-pppd/ctrl/ipoe/lua.c
@@ -0,0 +1,286 @@
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <pthread.h>
+
+/* Include the Lua API header files. */
+#include <lua.h>
+#include <lauxlib.h>
+#include <lualib.h>
+
+#include "events.h"
+#include "log.h"
+#include "utils.h"
+
+#include "ipoe.h"
+
+#include "memdebug.h"
+
+#define IPOE_PACKET4 "ipoe.packet4"
+
+static const char *conf_filename;
+static int serial;
+static int file_error;
+
+static __thread lua_State *L;
+static __thread int __serial;
+static pthread_key_t __key;
+
+static int packet4_hdr(lua_State *L);
+static int packet4_ifname(lua_State *L);
+static int packet4_option(lua_State *L);
+static int packet4_options(lua_State *L);
+static int packet4_agent_circuit_id(lua_State *L);
+static int packet4_agent_remote_id(lua_State *L);
+
+int luaopen_lpack(lua_State *L);
+
+static const struct luaL_reg packet4_lib [] = {
+ {"hdr", packet4_hdr},
+ {"ifname", packet4_ifname},
+ {"option", packet4_option},
+ {"options", packet4_options},
+ {"agent_circuit_id", packet4_agent_circuit_id},
+ {"agent_remote_id", packet4_agent_remote_id},
+ {NULL, NULL}
+};
+
+static int luaopen_packet4(lua_State *L)
+{
+ luaL_newmetatable(L, IPOE_PACKET4);
+
+ lua_pushstring(L, "__index");
+ lua_pushvalue(L, -2); /* pushes the metatable */
+ lua_settable(L, -3); /* metatable.__index = metatable */
+
+
+ luaI_openlib(L, NULL, packet4_lib, 0);
+
+ luaI_openlib(L, "packet4", packet4_lib, 0);
+
+ return 1;
+}
+
+static int packet4_hdr(lua_State *L)
+{
+ struct ipoe_session *ses = luaL_checkudata(L, 1, IPOE_PACKET4);
+ const char *name = luaL_checkstring(L, 2);
+ char str[20];
+ uint8_t *ptr;
+
+ if (!ses)
+ return 0;
+
+ if (!strcmp(name, "xid"))
+ lua_pushinteger(L, ses->dhcpv4_request->hdr->xid);
+ else if (!strcmp(name, "ciaddr")) {
+ u_inet_ntoa(ses->dhcpv4_request->hdr->ciaddr, str);
+ lua_pushstring(L, str);
+ } else if (!strcmp(name, "giaddr")) {
+ u_inet_ntoa(ses->dhcpv4_request->hdr->giaddr, str);
+ lua_pushstring(L, str);
+ } else if (!strcmp(name, "chaddr")) {
+ ptr = ses->dhcpv4_request->hdr->chaddr;
+ sprintf(str, "%02x:%02x:%02x:%02x:%02x:%02x",
+ ptr[0], ptr[1], ptr[2], ptr[3], ptr[4], ptr[5]);
+ lua_pushstring(L, str);
+ }
+
+ return 1;
+}
+
+static int packet4_ifname(lua_State *L)
+{
+ struct ipoe_session *ses = luaL_checkudata(L, 1, IPOE_PACKET4);
+
+ if (!ses)
+ return 0;
+
+ lua_pushstring(L, ses->serv->ifname);
+
+ return 1;
+}
+
+static int packet4_option(lua_State *L)
+{
+ struct ipoe_session *ses = luaL_checkudata(L, 1, IPOE_PACKET4);
+ int type = luaL_checkinteger(L, 2);
+ struct dhcpv4_option *opt;
+
+ list_for_each_entry(opt, &ses->dhcpv4_request->options, entry) {
+ if (opt->type == type) {
+ lua_pushlstring(L, (char *)opt->data, opt->len);
+ return 1;
+ }
+ }
+
+ lua_pushnil(L);
+
+ return 1;
+}
+
+static int packet4_options(lua_State *L)
+{
+ struct ipoe_session *ses = luaL_checkudata(L, 1, IPOE_PACKET4);
+ struct dhcpv4_option *opt;
+ int i = 1;
+
+ if (!ses)
+ return 0;
+
+ lua_newtable(L);
+
+ list_for_each_entry(opt, &ses->dhcpv4_request->options, entry) {
+ lua_pushinteger(L, opt->type);
+ lua_rawseti(L, -2, i++);
+ }
+
+ return 1;
+}
+
+static int packet4_agent_circuit_id(lua_State *L)
+{
+ struct ipoe_session *ses = luaL_checkudata(L, 1, IPOE_PACKET4);
+
+ if (!ses)
+ return 0;
+
+ if (ses->agent_circuit_id)
+ lua_pushlstring(L, (char *)ses->agent_circuit_id->data, ses->agent_circuit_id->len);
+ else
+ lua_pushnil(L);
+
+ return 1;
+}
+
+static int packet4_agent_remote_id(lua_State *L)
+{
+ struct ipoe_session *ses = luaL_checkudata(L, 1, IPOE_PACKET4);
+
+ if (!ses)
+ return 0;
+
+ if (ses->agent_remote_id)
+ lua_pushlstring(L, (char *)ses->agent_remote_id->data, ses->agent_remote_id->len);
+ else
+ lua_pushnil(L);
+
+ return 1;
+}
+
+static void init_lua()
+{
+ __serial = serial;
+
+ L = lua_open();
+
+ luaL_openlibs(L);
+
+ luaopen_lpack(L);
+ luaopen_packet4(L);
+
+ if (luaL_loadfile(L, conf_filename))
+ goto out_err;
+
+ if (lua_pcall(L, 0, 0, 0))
+ goto out_err;
+
+ lua_pushlightuserdata(L, L);
+ luaL_getmetatable(L, IPOE_PACKET4);
+ lua_setmetatable(L, -2);
+
+ lua_settop(L, 0);
+
+ file_error = 0;
+
+ pthread_setspecific(__key, L);
+
+ return;
+
+out_err:
+ file_error = 1;
+ log_ppp_error("ipoe: lua: %s\n", lua_tostring(L, -1));
+ lua_close(L);
+ L = NULL;
+}
+
+/*static void stackDump (lua_State *L) {
+ int i=lua_gettop(L);
+ printf(" ---------------- Stack Dump ----------------" );
+ while( i ) {
+ int t = lua_type(L, i);
+ switch (t) {
+ case LUA_TSTRING:
+ printf("%d:`%s'\n", i, lua_tostring(L, i));
+ break;
+ case LUA_TBOOLEAN:
+ printf("%d: %s\n",i,lua_toboolean(L, i) ? "true" : "false");
+ break;
+ case LUA_TNUMBER:
+ printf("%d: %g\n", i, lua_tonumber(L, i));
+ break;
+ default: printf("%d: %s\n", i, lua_typename(L, t)); break;
+ }
+ i--;
+ }
+ printf("--------------- Stack Dump Finished ---------------" );
+ }*/
+
+
+int ipoe_lua_set_username(struct ipoe_session *ses, const char *func)
+{
+ if (file_error && serial == __serial)
+ return -1;
+
+ if (L && serial != __serial) {
+ lua_close(L);
+ init_lua();
+ } else if (!L)
+ init_lua();
+
+ if (!L)
+ return -1;
+
+ lua_getglobal(L, func);
+ lua_pushlightuserdata(L, ses);
+
+ if (lua_pcall(L, 1, 1, 0)) {
+ log_ppp_error("ipoe: lua: %s\n", lua_tostring(L, -1));
+ goto out_err;
+ }
+
+ if (!lua_isstring(L, -1)) {
+ log_ppp_error("ipoe: lua: function '%s' must return a string\n", func);
+ goto out_err;
+ }
+
+ ses->ses.username = _strdup(lua_tostring(L, -1));
+
+ lua_settop(L, 0);
+
+ return 0;
+
+out_err:
+ file_error = 1;
+ lua_close(L);
+ L = NULL;
+ return -1;
+}
+
+static void load_config()
+{
+ conf_filename = conf_get_opt("ipoe", "lua-file");
+
+ serial++;
+}
+
+static void init()
+{
+ load_config();
+
+ pthread_key_create(&__key, (void (*)(void *))lua_close);
+
+ triton_event_register_handler(EV_CONFIG_RELOAD, (triton_event_func)load_config);
+}
+
+DEFINE_INIT(100, init);
diff --git a/accel-pppd/ctrl/ipoe/lua_lpack.c b/accel-pppd/ctrl/ipoe/lua_lpack.c
new file mode 100644
index 0000000..0dc3046
--- /dev/null
+++ b/accel-pppd/ctrl/ipoe/lua_lpack.c
@@ -0,0 +1,271 @@
+/*
+* lpack.c
+* a Lua library for packing and unpacking binary data
+* Luiz Henrique de Figueiredo <lhf@tecgraf.puc-rio.br>
+* 29 Jun 2007 19:27:20
+* This code is hereby placed in the public domain.
+* with contributions from Ignacio Castaņo <castanyo@yahoo.es> and
+* Roberto Ierusalimschy <roberto@inf.puc-rio.br>.
+*/
+
+#define OP_ZSTRING 'z' /* zero-terminated string */
+#define OP_BSTRING 'p' /* string preceded by length byte */
+#define OP_WSTRING 'P' /* string preceded by length word */
+#define OP_SSTRING 'a' /* string preceded by length size_t */
+#define OP_STRING 'A' /* string */
+#define OP_FLOAT 'f' /* float */
+#define OP_DOUBLE 'd' /* double */
+#define OP_NUMBER 'n' /* Lua number */
+#define OP_CHAR 'c' /* char */
+#define OP_BYTE 'b' /* byte = unsigned char */
+#define OP_SHORT 'h' /* short */
+#define OP_USHORT 'H' /* unsigned short */
+#define OP_INT 'i' /* int */
+#define OP_UINT 'I' /* unsigned int */
+#define OP_LONG 'l' /* long */
+#define OP_ULONG 'L' /* unsigned long */
+#define OP_LITTLEENDIAN '<' /* little endian */
+#define OP_BIGENDIAN '>' /* big endian */
+#define OP_NATIVE '=' /* native endian */
+
+#include <ctype.h>
+#include <stdint.h>
+#include <string.h>
+
+#include "lua.h"
+#include "lualib.h"
+#include "lauxlib.h"
+
+static void badcode(lua_State *L, int c)
+{
+ char s[]="bad code `?'";
+ s[sizeof(s)-3]=c;
+ luaL_argerror(L,1,s);
+}
+
+static int doendian(int c)
+{
+ int x=1;
+ int e=*(char*)&x;
+ if (c==OP_LITTLEENDIAN) return !e;
+ if (c==OP_BIGENDIAN) return e;
+ if (c==OP_NATIVE) return 0;
+ return 0;
+}
+
+static void doswap(int swap, void *p, size_t n)
+{
+ if (swap)
+ {
+ char *a=p;
+ int i,j;
+ for (i=0, j=n-1, n=n/2; n--; i++, j--)
+ {
+ char t=a[i]; a[i]=a[j]; a[j]=t;
+ }
+ }
+}
+
+#define UNPACKNUMBER(OP,T) \
+ case OP: \
+ { \
+ T a; \
+ int m=sizeof(a); \
+ if (i+m>len) goto done; \
+ memcpy(&a,s+i,m); \
+ i+=m; \
+ doswap(swap,&a,m); \
+ lua_pushinteger(L,(lua_Integer)a); \
+ ++n; \
+ break; \
+ }
+
+#define UNPACKSTRING(OP,T) \
+ case OP: \
+ { \
+ T l; \
+ int m=sizeof(l); \
+ if (i+m>len) goto done; \
+ memcpy(&l,s+i,m); \
+ doswap(swap,&l,m); \
+ if (i+m+l>len) goto done; \
+ i+=m; \
+ lua_pushlstring(L,s+i,l); \
+ i+=l; \
+ ++n; \
+ break; \
+ }
+
+static int l_unpack(lua_State *L) /** unpack(s,f,[init]) */
+{
+ size_t len;
+ const char *s=luaL_checklstring(L,1,&len);
+ const char *f=luaL_checkstring(L,2);
+ int i=luaL_optnumber(L,3,1)-1;
+ int n=0;
+ int swap=0;
+ lua_pushnil(L);
+ while (*f)
+ {
+ int c=*f++;
+ int N=1;
+ if (isdigit(*f))
+ {
+ N=0;
+ while (isdigit(*f)) N=10*N+(*f++)-'0';
+ if (N==0 && c==OP_STRING) { lua_pushliteral(L,""); ++n; }
+ }
+ while (N--) switch (c)
+ {
+ case OP_LITTLEENDIAN:
+ case OP_BIGENDIAN:
+ case OP_NATIVE:
+ {
+ swap=doendian(c);
+ N=0;
+ break;
+ }
+ case OP_STRING:
+ {
+ ++N;
+ if (i+N>len) goto done;
+ lua_pushlstring(L,s+i,N);
+ i+=N;
+ ++n;
+ N=0;
+ break;
+ }
+ case OP_ZSTRING:
+ {
+ size_t l;
+ if (i>=len) goto done;
+ l=strlen(s+i);
+ lua_pushlstring(L,s+i,l);
+ i+=l+1;
+ ++n;
+ break;
+ }
+ UNPACKSTRING(OP_BSTRING, unsigned char)
+ UNPACKSTRING(OP_WSTRING, unsigned short)
+ UNPACKSTRING(OP_SSTRING, size_t)
+ UNPACKNUMBER(OP_NUMBER, lua_Number)
+ UNPACKNUMBER(OP_DOUBLE, double)
+ UNPACKNUMBER(OP_FLOAT, float)
+ UNPACKNUMBER(OP_CHAR, int8_t)
+ UNPACKNUMBER(OP_BYTE, uint8_t)
+ UNPACKNUMBER(OP_SHORT, int16_t)
+ UNPACKNUMBER(OP_USHORT, uint16_t)
+ UNPACKNUMBER(OP_INT, int32_t)
+ UNPACKNUMBER(OP_UINT, uint32_t)
+ UNPACKNUMBER(OP_LONG, int64_t)
+ UNPACKNUMBER(OP_ULONG, uint64_t)
+ case ' ': case ',':
+ break;
+ default:
+ badcode(L,c);
+ break;
+ }
+ }
+done:
+ lua_pushnumber(L,i+1);
+ lua_replace(L,-n-2);
+ return n+1;
+}
+
+#define PACKNUMBER(OP,T) \
+ case OP: \
+ { \
+ T a=(T)luaL_checknumber(L,i++); \
+ doswap(swap,&a,sizeof(a)); \
+ luaL_addlstring(&b,(void*)&a,sizeof(a)); \
+ break; \
+ }
+
+#define PACKSTRING(OP,T) \
+ case OP: \
+ { \
+ size_t l; \
+ const char *a=luaL_checklstring(L,i++,&l); \
+ T ll=(T)l; \
+ doswap(swap,&ll,sizeof(ll)); \
+ luaL_addlstring(&b,(void*)&ll,sizeof(ll)); \
+ luaL_addlstring(&b,a,l); \
+ break; \
+ }
+
+static int l_pack(lua_State *L) /** pack(f,...) */
+{
+ int i=2;
+ const char *f=luaL_checkstring(L,1);
+ int swap=0;
+ luaL_Buffer b;
+ luaL_buffinit(L,&b);
+ while (*f)
+ {
+ int c=*f++;
+ int N=1;
+ if (isdigit(*f))
+ {
+ N=0;
+ while (isdigit(*f)) N=10*N+(*f++)-'0';
+ }
+ while (N--) switch (c)
+ {
+ case OP_LITTLEENDIAN:
+ case OP_BIGENDIAN:
+ case OP_NATIVE:
+ {
+ swap=doendian(c);
+ N=0;
+ break;
+ }
+ case OP_STRING:
+ case OP_ZSTRING:
+ {
+ size_t l;
+ const char *a=luaL_checklstring(L,i++,&l);
+ luaL_addlstring(&b,a,l+(c==OP_ZSTRING));
+ break;
+ }
+ PACKSTRING(OP_BSTRING, unsigned char)
+ PACKSTRING(OP_WSTRING, unsigned short)
+ PACKSTRING(OP_SSTRING, size_t)
+ PACKNUMBER(OP_NUMBER, lua_Number)
+ PACKNUMBER(OP_DOUBLE, double)
+ PACKNUMBER(OP_FLOAT, float)
+ PACKNUMBER(OP_CHAR, int8_t)
+ PACKNUMBER(OP_BYTE, uint8_t)
+ PACKNUMBER(OP_SHORT, int16_t)
+ PACKNUMBER(OP_USHORT, uint16_t)
+ PACKNUMBER(OP_INT, int32_t)
+ PACKNUMBER(OP_UINT, uint32_t)
+ PACKNUMBER(OP_LONG, int64_t)
+ PACKNUMBER(OP_ULONG, uint64_t)
+ case ' ': case ',':
+ break;
+ default:
+ badcode(L,c);
+ break;
+ }
+ }
+ luaL_pushresult(&b);
+ return 1;
+}
+
+static const luaL_reg R[] =
+{
+ {"pack", l_pack},
+ {"unpack", l_unpack},
+ {NULL, NULL}
+};
+
+int luaopen_lpack(lua_State *L)
+{
+#ifdef USE_GLOBALS
+ lua_register(L,"bpack",l_pack);
+ lua_register(L,"bunpack",l_unpack);
+#else
+ luaI_openlib(L, LUA_STRLIBNAME, R, 0);
+#endif
+ return 0;
+}
diff --git a/accel-pppd/ctrl/l2tp/l2tp.c b/accel-pppd/ctrl/l2tp/l2tp.c
index 5bba25e..e85876e 100644
--- a/accel-pppd/ctrl/l2tp/l2tp.c
+++ b/accel-pppd/ctrl/l2tp/l2tp.c
@@ -94,7 +94,7 @@ struct l2tp_conn_t
int state1;
int state2;
- struct ppp_ctrl_t ctrl;
+ struct ap_ctrl ctrl;
struct ppp_t ppp;
};
@@ -130,7 +130,7 @@ static void l2tp_disconnect(struct l2tp_conn_t *conn)
if (conn->state == STATE_PPP) {
__sync_sub_and_fetch(&stat_active, 1);
conn->state = STATE_FIN;
- ppp_terminate(&conn->ppp, TERM_USER_REQUEST, 1);
+ ap_session_terminate(&conn->ppp.ses, TERM_USER_REQUEST, 1);
} else if (conn->state != STATE_FIN)
__sync_sub_and_fetch(&stat_starting, 1);
@@ -144,7 +144,7 @@ static void l2tp_disconnect(struct l2tp_conn_t *conn)
if (conn->tunnel_fd != -1)
close(conn->tunnel_fd);
- triton_event_fire(EV_CTRL_FINISHED, &conn->ppp);
+ triton_event_fire(EV_CTRL_FINISHED, &conn->ppp.ses);
log_ppp_info1("disconnected\n");
@@ -156,8 +156,8 @@ static void l2tp_disconnect(struct l2tp_conn_t *conn)
l2tp_packet_free(pack);
}
- if (conn->ppp.chan_name)
- _free(conn->ppp.chan_name);
+ if (conn->ppp.ses.chan_name)
+ _free(conn->ppp.ses.chan_name);
if (conn->challenge_len)
_free(conn->challenge.octets);
_free(conn->ctrl.calling_station_id);
@@ -193,8 +193,9 @@ out_err:
return -1;
}
-static void l2tp_ppp_started(struct ppp_t *ppp)
+static void l2tp_ppp_started(struct ap_session *ses)
{
+ struct ppp_t *ppp = container_of(ses, typeof(*ppp), ses);
struct l2tp_conn_t *conn = container_of(ppp, typeof(*conn), ppp);
log_ppp_debug("l2tp: ppp started\n");
@@ -203,8 +204,9 @@ static void l2tp_ppp_started(struct ppp_t *ppp)
triton_timer_add(&conn->ctx, &conn->hello_timer, 0);
}
-static void l2tp_ppp_finished(struct ppp_t *ppp)
+static void l2tp_ppp_finished(struct ap_session *ses)
{
+ struct ppp_t *ppp = container_of(ses, typeof(*ppp), ses);
struct l2tp_conn_t *conn = container_of(ppp, typeof(*conn), ppp);
log_ppp_debug("l2tp: ppp finished\n");
@@ -223,7 +225,7 @@ static void l2tp_conn_close(struct triton_context_t *ctx)
if (conn->state == STATE_PPP) {
__sync_sub_and_fetch(&stat_active, 1);
conn->state = STATE_FIN;
- ppp_terminate(&conn->ppp, TERM_ADMIN_RESET, 1);
+ ap_session_terminate(&conn->ppp.ses, TERM_ADMIN_RESET, 1);
}
if (l2tp_terminate(conn, 0, 0))
@@ -343,11 +345,11 @@ static int l2tp_tunnel_alloc(struct l2tp_serv_t *serv, struct l2tp_packet_t *pac
u_inet_ntoa(addr.sin_addr.s_addr, conn->ctrl.called_station_id);
ppp_init(&conn->ppp);
- conn->ppp.ctrl = &conn->ctrl;
+ conn->ppp.ses.ctrl = &conn->ctrl;
conn->ppp.fd = -1;
conn->tunnel_fd = -1;
- triton_context_register(&conn->ctx, &conn->ppp);
+ triton_context_register(&conn->ctx, &conn->ppp.ses);
triton_md_register_handler(&conn->ctx, &conn->hnd);
triton_md_enable_handler(&conn->hnd, MD_MODE_READ);
triton_context_wakeup(&conn->ctx);
@@ -419,9 +421,9 @@ static int l2tp_connect(struct l2tp_conn_t *conn)
return -1;
}
- conn->ppp.chan_name = _strdup(inet_ntoa(conn->addr.sin_addr));
+ conn->ppp.ses.chan_name = _strdup(inet_ntoa(conn->addr.sin_addr));
- triton_event_fire(EV_CTRL_STARTED, &conn->ppp);
+ triton_event_fire(EV_CTRL_STARTED, &conn->ppp.ses);
if (establish_ppp(&conn->ppp))
return -1;
@@ -654,7 +656,7 @@ static int l2tp_recv_SCCRQ(struct l2tp_serv_t *serv, struct l2tp_packet_t *pack,
struct l2tp_attr_t *router_id = NULL;
struct l2tp_attr_t *challenge = NULL;
- if (ppp_shutdown)
+ if (ap_shutdown)
return 0;
if (triton_module_loaded("connlimit") && connlimit_check(cl_key_from_ipv4(pack->addr.sin_addr.s_addr)))
@@ -863,7 +865,7 @@ static int l2tp_recv_CDN(struct l2tp_conn_t *conn, struct l2tp_packet_t *pack)
if (conn->state == STATE_PPP) {
__sync_sub_and_fetch(&stat_active, 1);
conn->state = STATE_FIN;
- ppp_terminate(&conn->ppp, TERM_USER_REQUEST, 1);
+ ap_session_terminate(&conn->ppp.ses, TERM_USER_REQUEST, 1);
}
if (l2tp_terminate(conn, 0, 0))
diff --git a/accel-pppd/ctrl/pppoe/pppoe.c b/accel-pppd/ctrl/pppoe/pppoe.c
index 079ef51..9608e9a 100644
--- a/accel-pppd/ctrl/pppoe/pppoe.c
+++ b/accel-pppd/ctrl/pppoe/pppoe.c
@@ -48,7 +48,7 @@ struct pppoe_conn_t
struct pppoe_tag *tr101;
uint8_t cookie[COOKIE_LENGTH];
- struct ppp_ctrl_t ctrl;
+ struct ap_ctrl ctrl;
struct ppp_t ppp;
#ifdef RADIUS
struct rad_plugin_t radius;
@@ -112,7 +112,7 @@ static void disconnect(struct pppoe_conn_t *conn)
if (conn->ppp_started) {
dpado_check_prev(__sync_fetch_and_sub(&stat_active, 1));
conn->ppp_started = 0;
- ppp_terminate(&conn->ppp, TERM_USER_REQUEST, 1);
+ ap_session_terminate(&conn->ppp.ses, TERM_USER_REQUEST, 1);
}
pppoe_send_PADT(conn);
@@ -120,7 +120,7 @@ static void disconnect(struct pppoe_conn_t *conn)
close(conn->disc_sock);
- triton_event_fire(EV_CTRL_FINISHED, &conn->ppp);
+ triton_event_fire(EV_CTRL_FINISHED, &conn->ppp.ses);
log_ppp_info1("disconnected\n");
@@ -149,13 +149,14 @@ static void disconnect(struct pppoe_conn_t *conn)
mempool_free(conn);
}
-static void ppp_started(struct ppp_t *ppp)
+static void ppp_started(struct ap_session *ses)
{
log_ppp_debug("pppoe: ppp started\n");
}
-static void ppp_finished(struct ppp_t *ppp)
+static void ppp_finished(struct ap_session *ses)
{
+ struct ppp_t *ppp = container_of(ses, typeof(*ppp), ses);
struct pppoe_conn_t *conn = container_of(ppp, typeof(*conn), ppp);
log_ppp_debug("pppoe: ppp finished\n");
@@ -172,7 +173,7 @@ 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, TERM_ADMIN_RESET, 0);
+ ap_session_terminate(&conn->ppp.ses, TERM_ADMIN_RESET, 0);
else
disconnect(conn);
}
@@ -261,6 +262,7 @@ static struct pppoe_conn_t *allocate_channel(struct pppoe_serv_t *serv, const ui
conn->ctrl.ctx = &conn->ctx;
conn->ctrl.started = ppp_started;
conn->ctrl.finished = ppp_finished;
+ conn->ctrl.terminate = ppp_terminate;
conn->ctrl.max_mtu = MAX_PPPOE_MTU;
conn->ctrl.type = CTRL_TYPE_PPPOE;
conn->ctrl.name = "pppoe";
@@ -285,14 +287,14 @@ static struct pppoe_conn_t *allocate_channel(struct pppoe_serv_t *serv, const ui
ppp_init(&conn->ppp);
- conn->ppp.ctrl = &conn->ctrl;
- conn->ppp.chan_name = conn->ctrl.calling_station_id;
+ conn->ppp.ses.ctrl = &conn->ctrl;
+ conn->ppp.ses.chan_name = conn->ctrl.calling_station_id;
- triton_context_register(&conn->ctx, &conn->ppp);
+ triton_context_register(&conn->ctx, &conn->ppp.ses);
triton_context_wakeup(&conn->ctx);
- triton_event_fire(EV_CTRL_STARTING, &conn->ppp);
- triton_event_fire(EV_CTRL_STARTED, &conn->ppp);
+ triton_event_fire(EV_CTRL_STARTING, &conn->ppp.ses);
+ triton_event_fire(EV_CTRL_STARTED, &conn->ppp.ses);
conn->disc_sock = dup(serv->hnd.fd);
@@ -334,7 +336,7 @@ static void connect_channel(struct pppoe_conn_t *conn)
if (conn->tr101 && triton_module_loaded("radius")) {
conn->radius.send_access_request = pppoe_rad_send_access_request;
conn->radius.send_accounting_request = pppoe_rad_send_accounting_request;
- rad_register_plugin(&conn->ppp, &conn->radius);
+ rad_register_plugin(&conn->ppp.ses, &conn->radius);
}
#endif
@@ -704,7 +706,7 @@ static void pado_timer(struct triton_timer_t *t)
{
struct delayed_pado_t *pado = container_of(t, typeof(*pado), timer);
- if (!ppp_shutdown)
+ if (!ap_shutdown)
pppoe_send_PADO(pado->serv, pado->addr, pado->host_uniq, pado->relay_sid, pado->service_name);
free_delayed_pado(pado);
@@ -775,7 +777,7 @@ static void pppoe_recv_PADI(struct pppoe_serv_t *serv, uint8_t *pack, int size)
__sync_add_and_fetch(&stat_PADI_recv, 1);
- if (ppp_shutdown || pado_delay == -1)
+ if (ap_shutdown || pado_delay == -1)
return;
if (check_padi_limit(serv, ethhdr->h_source)) {
@@ -888,7 +890,7 @@ static void pppoe_recv_PADR(struct pppoe_serv_t *serv, uint8_t *pack, int size)
__sync_add_and_fetch(&stat_PADR_recv, 1);
- if (ppp_shutdown)
+ if (ap_shutdown)
return;
if (!memcmp(ethhdr->h_dest, bc_addr, ETH_ALEN)) {
@@ -974,7 +976,7 @@ static void pppoe_recv_PADR(struct pppoe_serv_t *serv, uint8_t *pack, int size)
pthread_mutex_lock(&serv->lock);
conn = find_channel(serv, (uint8_t *)ac_cookie_tag->tag_data);
- if (conn && !conn->ppp.username) {
+ if (conn && !conn->ppp.ses.username) {
__sync_add_and_fetch(&stat_PADR_dup_recv, 1);
pppoe_send_PADS(conn);
}
@@ -1279,7 +1281,7 @@ out_err:
static void _conn_stop(struct pppoe_conn_t *conn)
{
- ppp_terminate(&conn->ppp, TERM_ADMIN_RESET, 0);
+ ap_session_terminate(&conn->ppp.ses, TERM_ADMIN_RESET, 0);
}
static void _server_stop(struct pppoe_serv_t *serv)
diff --git a/accel-pppd/ctrl/pptp/pptp.c b/accel-pppd/ctrl/pptp/pptp.c
index 2f0c5ef..dddf5ed 100644
--- a/accel-pppd/ctrl/pptp/pptp.c
+++ b/accel-pppd/ctrl/pptp/pptp.c
@@ -50,7 +50,7 @@ struct pptp_conn_t
int out_size;
int out_pos;
- struct ppp_ctrl_t ctrl;
+ struct ap_ctrl ctrl;
struct ppp_t ppp;
};
@@ -68,8 +68,8 @@ static unsigned int stat_active;
static int pptp_read(struct triton_md_handler_t *h);
static int pptp_write(struct triton_md_handler_t *h);
static void pptp_timeout(struct triton_timer_t *);
-static void ppp_started(struct ppp_t *);
-static void ppp_finished(struct ppp_t *);
+static void ppp_started(struct ap_session *);
+static void ppp_finished(struct ap_session *);
static void disconnect(struct pptp_conn_t *conn)
{
@@ -87,18 +87,18 @@ static void disconnect(struct pptp_conn_t *conn)
if (conn->state == STATE_PPP) {
__sync_sub_and_fetch(&stat_active, 1);
conn->state = STATE_CLOSE;
- ppp_terminate(&conn->ppp, TERM_LOST_CARRIER, 1);
+ ap_session_terminate(&conn->ppp.ses, TERM_LOST_CARRIER, 1);
} else if (conn->state != STATE_CLOSE)
__sync_sub_and_fetch(&stat_starting, 1);
- triton_event_fire(EV_CTRL_FINISHED, &conn->ppp);
+ triton_event_fire(EV_CTRL_FINISHED, &conn->ppp.ses);
log_ppp_info1("disconnected\n");
triton_context_unregister(&conn->ctx);
- if (conn->ppp.chan_name)
- _free(conn->ppp.chan_name);
+ if (conn->ppp.ses.chan_name)
+ _free(conn->ppp.ses.chan_name);
_free(conn->in_buf);
_free(conn->out_buf);
@@ -325,9 +325,9 @@ static int pptp_out_call_rqst(struct pptp_conn_t *conn)
conn->call_id = src_addr.sa_addr.pptp.call_id;
conn->peer_call_id = msg->call_id;
conn->ppp.fd = pptp_sock;
- conn->ppp.chan_name = _strdup(inet_ntoa(dst_addr.sa_addr.pptp.sin_addr));
+ conn->ppp.ses.chan_name = _strdup(inet_ntoa(dst_addr.sa_addr.pptp.sin_addr));
- triton_event_fire(EV_CTRL_STARTED, &conn->ppp);
+ triton_event_fire(EV_CTRL_STARTED, &conn->ppp.ses);
if (establish_ppp(&conn->ppp)) {
close(pptp_sock);
@@ -379,7 +379,7 @@ static int pptp_call_clear_rqst(struct pptp_conn_t *conn)
if (conn->state == STATE_PPP) {
__sync_sub_and_fetch(&stat_active, 1);
conn->state = STATE_CLOSE;
- ppp_terminate(&conn->ppp, TERM_USER_REQUEST, 1);
+ ap_session_terminate(&conn->ppp.ses, TERM_USER_REQUEST, 1);
}
return send_pptp_call_disconnect_notify(conn, 4);
@@ -561,7 +561,7 @@ static void pptp_close(struct triton_context_t *ctx)
if (conn->state == STATE_PPP) {
__sync_sub_and_fetch(&stat_active, 1);
conn->state = STATE_CLOSE;
- ppp_terminate(&conn->ppp, TERM_ADMIN_RESET, 1);
+ ap_session_terminate(&conn->ppp.ses, TERM_ADMIN_RESET, 1);
if (send_pptp_call_disconnect_notify(conn, 3)) {
triton_context_call(&conn->ctx, (void (*)(void*))disconnect, conn);
return;
@@ -578,12 +578,13 @@ static void pptp_close(struct triton_context_t *ctx)
else
triton_timer_add(ctx, &conn->timeout_timer, 0);
}
-static void ppp_started(struct ppp_t *ppp)
+static void ppp_started(struct ap_session *ses)
{
log_ppp_debug("pptp: ppp started\n");
}
-static void ppp_finished(struct ppp_t *ppp)
+static void ppp_finished(struct ap_session *ses)
{
+ struct ppp_t *ppp = container_of(ses, typeof(*ppp), ses);
struct pptp_conn_t *conn = container_of(ppp, typeof(*conn), ppp);
if (conn->state != STATE_CLOSE) {
@@ -628,7 +629,7 @@ static int pptp_connect(struct triton_md_handler_t *h)
continue;
}
- if (ppp_shutdown) {
+ if (ap_shutdown) {
close(sock);
continue;
}
@@ -667,6 +668,7 @@ static int pptp_connect(struct triton_md_handler_t *h)
conn->ctrl.ctx = &conn->ctx;
conn->ctrl.started = ppp_started;
conn->ctrl.finished = ppp_finished;
+ conn->ctrl.terminate = ppp_terminate;
conn->ctrl.max_mtu = PPTP_MAX_MTU;
conn->ctrl.type = CTRL_TYPE_PPTP;
conn->ctrl.name = "pptp";
@@ -679,15 +681,15 @@ static int pptp_connect(struct triton_md_handler_t *h)
u_inet_ntoa(addr.sin_addr.s_addr, conn->ctrl.called_station_id);
ppp_init(&conn->ppp);
- conn->ppp.ctrl = &conn->ctrl;
+ conn->ppp.ses.ctrl = &conn->ctrl;
- triton_context_register(&conn->ctx, &conn->ppp);
+ triton_context_register(&conn->ctx, &conn->ppp.ses);
triton_md_register_handler(&conn->ctx, &conn->hnd);
triton_md_enable_handler(&conn->hnd,MD_MODE_READ);
triton_timer_add(&conn->ctx, &conn->timeout_timer, 0);
triton_context_wakeup(&conn->ctx);
- triton_event_fire(EV_CTRL_STARTING, &conn->ppp);
+ triton_event_fire(EV_CTRL_STARTING, &conn->ppp.ses);
__sync_add_and_fetch(&stat_starting, 1);
}