From 4080b4e1ffdb1482ab26557eb46a8121d93ac584 Mon Sep 17 00:00:00 2001 From: Kozlov Dmitry Date: Fri, 22 Jun 2012 18:11:19 +0400 Subject: initial ipoe implementation --- accel-pppd/ctrl/ipoe/CMakeLists.txt | 24 ++ accel-pppd/ctrl/ipoe/dhcpv4.c | 538 ++++++++++++++++++++++++++ accel-pppd/ctrl/ipoe/dhcpv4.h | 89 +++++ accel-pppd/ctrl/ipoe/dhcpv4_options.c | 290 ++++++++++++++ accel-pppd/ctrl/ipoe/ipoe.c | 688 ++++++++++++++++++++++++++++++++++ accel-pppd/ctrl/ipoe/ipoe.h | 54 +++ accel-pppd/ctrl/ipoe/lua.c | 253 +++++++++++++ accel-pppd/ctrl/ipoe/lua_lpack.c | 271 +++++++++++++ 8 files changed, 2207 insertions(+) create mode 100644 accel-pppd/ctrl/ipoe/CMakeLists.txt create mode 100644 accel-pppd/ctrl/ipoe/dhcpv4.c create mode 100644 accel-pppd/ctrl/ipoe/dhcpv4.h create mode 100644 accel-pppd/ctrl/ipoe/dhcpv4_options.c create mode 100644 accel-pppd/ctrl/ipoe/ipoe.c create mode 100644 accel-pppd/ctrl/ipoe/ipoe.h create mode 100644 accel-pppd/ctrl/ipoe/lua.c create mode 100644 accel-pppd/ctrl/ipoe/lua_lpack.c (limited to 'accel-pppd/ctrl/ipoe') diff --git a/accel-pppd/ctrl/ipoe/CMakeLists.txt b/accel-pppd/ctrl/ipoe/CMakeLists.txt new file mode 100644 index 0000000..e2b71cb --- /dev/null +++ b/accel-pppd/ctrl/ipoe/CMakeLists.txt @@ -0,0 +1,24 @@ +INCLUDE_DIRECTORIES(${CMAKE_CURRENT_SOURCE_DIR}) + +SET(sources + ipoe.c + dhcpv4.c + dhcpv4_options.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/dhcpv4.c b/accel-pppd/ctrl/ipoe/dhcpv4.c new file mode 100644 index 0000000..2955d6c --- /dev/null +++ b/accel-pppd/ctrl/ipoe/dhcpv4.c @@ -0,0 +1,538 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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 mempool_t pack_pool; +static mempool_t opt_pool; + +static int dhcpv4_read(struct triton_md_handler_t *h); + +struct dhcpv4_serv *dhcpv4_create(struct triton_context_t *ctx, const char *ifname) +{ + struct dhcpv4_serv *serv; + int sock, raw_sock; + struct sockaddr_in addr; + struct sockaddr_ll ll_addr; + struct ifreq ifr; + int f = 1; + + 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; + } + + memcpy(serv->hwaddr, ifr.ifr_hwaddr.sa_data, ETH_ALEN); + + fcntl(raw_sock, F_SETFL, O_NONBLOCK); + fcntl(raw_sock, F_SETFD, fcntl(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)); + + serv->ctx = ctx; + serv->hnd.fd = sock; + serv->hnd.read = dhcpv4_read; + serv->raw_sock = raw_sock; + + 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); + _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("ciaddr=%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(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; +} + +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, struct ap_session *ses, int lease_time) +{ + 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->yiaddr = ses->ipv4->peer_addr; + pack->hdr->siaddr = ses->ipv4->addr; + + if (dhcpv4_packet_add_opt(pack, 53, &msg_type, 1)) + goto out_err; + + if (dhcpv4_packet_add_opt(pack, 54, &ses->ipv4->addr, 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, &ses->ipv4->addr, 4)) + goto out_err; + + val = htonl(~((1 << (32 - ses->ipv4->mask)) - 1)); + if (dhcpv4_packet_add_opt(pack, 1, &val, 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, ses->ipv4->addr, ses->ipv4->peer_addr); + + 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) +{ + + return 0; +} + +static void load_config() +{ + const char *opt; + + opt = conf_get_opt("ipoe", "verbose"); + if (opt) + conf_verbose = atoi(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..52e90a3 --- /dev/null +++ b/accel-pppd/ctrl/ipoe/dhcpv4.h @@ -0,0 +1,89 @@ +#ifndef __DHCPV4_H +#define __DHCPV4_H + +#include +#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_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 ap_session; + +struct dhcpv4_serv *dhcpv4_create(struct triton_context_t *ctx, const char *ifname); +void dhcpv4_free(struct dhcpv4_serv *); + + +int dhcpv4_send_reply(int msg_type, struct dhcpv4_serv *serv, struct dhcpv4_packet *req, struct ap_session *ses, 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, ...)); + +#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 +#include +#include +#include +#include +#include + +#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/ipoe.c b/accel-pppd/ctrl/ipoe/ipoe.c new file mode 100644 index 0000000..2258c62 --- /dev/null +++ b/accel-pppd/ctrl/ipoe/ipoe.c @@ -0,0 +1,688 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#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 + +static int conf_dhcpv4 = 1; +//static int conf_dhcpv6; +static int conf_username; + +#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; +}; + +static void ipoe_session_finished(struct ap_session *s); + +static struct ipoe_session *ipoe_session_lookup(struct ipoe_serv *serv, struct dhcpv4_packet *pack) +{ + struct ipoe_session *ses; + + list_for_each_entry(ses, &serv->sessions, entry) { + if (pack->hdr->xid != ses->xid) + continue; + + 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->client_id && !ses->client_id) + continue; + + if (!pack->agent_circuit_id && ses->agent_circuit_id) + continue; + + if (!pack->agent_remote_id && ses->agent_remote_id) + continue; + + if (!pack->client_id && ses->client_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; + } + + 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; + + return ses; + } + + return NULL; +} + +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; + + if (ses->serv->opt_single) + strncpy(ses->ses.ifname, ses->serv->ifname, AP_IFNAME_LEN); + + 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; + } + + if (ses->dhcpv4_request) { + 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; + + dhcpv4_send_reply(DHCPOFFER, ses->serv->dhcpv4, ses->dhcpv4_request, &ses->ses, conf_lease_time); + + dhcpv4_packet_free(ses->dhcpv4_request); + ses->dhcpv4_request = NULL; + } + + ses->timer.expire = ipoe_session_timeout; + ses->timer.period = conf_offer_timeout * 1000; + triton_timer_add(&ses->ctx, &ses->timer, 0); +} + +static void ipoe_session_activate(struct ipoe_session *ses) +{ + ap_session_activate(&ses->ses); + + if (ses->ses.state == AP_STATE_ACTIVE) + dhcpv4_send_reply(DHCPACK, ses->serv->dhcpv4, ses->dhcpv4_request, &ses->ses, 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"); + + triton_timer_del(&ses->timer); + + ses->timer.expire = ipoe_session_timeout; + ses->timer.period = conf_lease_timeout * 1000; + triton_timer_add(&ses->ctx, &ses->timer, 0); +} + +static void ipoe_session_free(struct ipoe_session *ses) +{ + if (ses->timer.tpd) + triton_timer_del(&ses->timer); + + triton_context_unregister(&ses->ctx); + + if (ses->data) + _free(ses->data); + + mempool_free(ses); +} + +static void ipoe_session_finished(struct ap_session *s) +{ + struct ipoe_session *ses = container_of(s, typeof(*ses), ses); + + log_ppp_debug("ipoe: session finished\n"); + + pthread_mutex_lock(&ses->serv->lock); + list_del(&ses->entry); + pthread_mutex_unlock(&ses->serv->lock); + + 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(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->dhcpv4_request = pack; + + ses->xid = pack->hdr->xid; + memcpy(ses->hwaddr, pack->hdr->chaddr, 6); + ses->giaddr = pack->hdr->giaddr; + + 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 = 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_dhcpv4_recv(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; + + pthread_mutex_lock(&serv->lock); + if (pack->msg_type == DHCPDISCOVER) { + ses = ipoe_session_lookup(serv, pack); + if (!ses) { + ses = ipoe_session_create(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.state == AP_STATE_ACTIVE) + dhcpv4_send_reply(DHCPOFFER, dhcpv4, pack, &ses->ses, 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 (!ses->ses.ipv4 || pack->server_id != ses->ses.ipv4->addr || pack->request_ip != ses->ses.ipv4->peer_addr) { + if (conf_verbose) { + log_info2("recv "); + dhcpv4_print_packet(pack, log_info2); + } + + if (ses->ses.ipv4 && pack->request_ip != ses->ses.ipv4->peer_addr) + 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 (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); + } + } + } + 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 void ipoe_serv_close(struct triton_context_t *ctx) +{ + struct ipoe_serv *serv = container_of(ctx, typeof(*serv), ctx); + + 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 ipoe_drop_sessions(struct ipoe_serv *serv) +{ + +} + +static void add_interface(const char *ifname, int ifindex, const char *opt) +{ + int opt_single; + const char *ptr; + struct ipoe_serv *serv; + + ptr = strstr(opt, ",single"); + if (ptr) { + if (ptr[7] && ptr[7] != ',') + goto out_err_parse; + opt_single = 1; + } else + opt_single = 0; + + + list_for_each_entry(serv, &serv_list, entry) { + if (strcmp(ifname, serv->ifname) == 0) { + serv->active = 1; + serv->ifindex = ifindex; + if (opt_single && !serv->opt_single) + ipoe_drop_sessions(serv); + serv->opt_single = opt_single; + return; + } + } + + serv = _malloc(sizeof(*serv)); + memset(serv, 0, sizeof(*serv)); + serv->ifname = _strdup(ifname); + serv->ifindex = ifindex; + serv->opt_single = opt_single; + serv->opt_dhcpv4 = conf_dhcpv4; + 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); + if (serv->dhcpv4) + serv->dhcpv4->recv = ipoe_dhcpv4_recv; + } + + triton_context_wakeup(&serv->ctx); + + return; + +out_err_parse: + log_error("ipoe: failed to parse '%s'\n", opt); +} + +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, §->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) { + list_del(&serv->entry); + triton_context_call(&serv->ctx, (triton_event_func)ipoe_serv_close, &serv->ctx); + } + } +} + +static void load_config(void) +{ + const char *opt; + struct conf_sect_t *s = conf_get_section("ipoe"); + + if (!s) + return; + + load_interfaces(s); + + 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); +} + +static void ipoe_init(void) +{ + ses_pool = mempool_create(sizeof(struct ipoe_session)); + + 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..adbc5cf --- /dev/null +++ b/accel-pppd/ctrl/ipoe/ipoe.h @@ -0,0 +1,54 @@ +#ifndef __IPOE_H +#define __IPOE_H + +#include +#include + +#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; + int opt_single; + int opt_dhcpv4; + struct list_head sessions; + struct dhcpv4_serv *dhcpv4; + pthread_mutex_t lock; +}; + +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; + uint8_t *data; + struct dhcpv4_packet *dhcpv4_request; +}; + +#ifdef USE_LUA +int ipoe_lua_set_username(struct ipoe_session *, const char *func); +#endif + +#endif + diff --git a/accel-pppd/ctrl/ipoe/lua.c b/accel-pppd/ctrl/ipoe/lua.c new file mode 100644 index 0000000..77beca6 --- /dev/null +++ b/accel-pppd/ctrl/ipoe/lua.c @@ -0,0 +1,253 @@ +#include +#include +#include +#include + +/* Include the Lua API header files. */ +#include +#include +#include + +#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; + + 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; +} + +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); + luaL_getmetatable(L, IPOE_PACKET4); + lua_setmetatable(L, -2); + + if (lua_pcall(L, 1, 1, 0)) { + log_ppp_error("ipoe: lua: %s\n", lua_tostring(L, -1)); + return -1; + } + + if (!lua_isstring(L, -1)) { + log_ppp_error("ipoe: lua: function '%s' must return a string\n", func); + return -1; + } + + ses->ses.username = _strdup(lua_tostring(L, -1)); + + lua_pop(L, 1); + + return 0; +} + +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..22d3477 --- /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 +* 29 Jun 2007 19:27:20 +* This code is hereby placed in the public domain. +* with contributions from Ignacio Castaņo and +* Roberto Ierusalimschy . +*/ + +#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 +#include +#include + +#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_pushnumber(L,(lua_Number)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; +} -- cgit v1.2.3