diff options
Diffstat (limited to 'accel-pppd/ctrl')
-rw-r--r-- | accel-pppd/ctrl/CMakeLists.txt | 1 | ||||
-rw-r--r-- | accel-pppd/ctrl/ipoe/CMakeLists.txt | 26 | ||||
-rw-r--r-- | accel-pppd/ctrl/ipoe/backup.c | 217 | ||||
-rw-r--r-- | accel-pppd/ctrl/ipoe/dhcpv4.c | 756 | ||||
-rw-r--r-- | accel-pppd/ctrl/ipoe/dhcpv4.h | 106 | ||||
-rw-r--r-- | accel-pppd/ctrl/ipoe/dhcpv4_options.c | 290 | ||||
l--------- | accel-pppd/ctrl/ipoe/if_ipoe.h | 1 | ||||
-rw-r--r-- | accel-pppd/ctrl/ipoe/ipoe.c | 1159 | ||||
-rw-r--r-- | accel-pppd/ctrl/ipoe/ipoe.h | 84 | ||||
-rw-r--r-- | accel-pppd/ctrl/ipoe/ipoe_netlink.c | 480 | ||||
-rw-r--r-- | accel-pppd/ctrl/ipoe/lua.c | 286 | ||||
-rw-r--r-- | accel-pppd/ctrl/ipoe/lua_lpack.c | 271 | ||||
-rw-r--r-- | accel-pppd/ctrl/l2tp/l2tp.c | 32 | ||||
-rw-r--r-- | accel-pppd/ctrl/pppoe/pppoe.c | 36 | ||||
-rw-r--r-- | accel-pppd/ctrl/pptp/pptp.c | 36 |
15 files changed, 3732 insertions, 49 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, §->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, §->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 fa76048..b54e5e3 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); @@ -436,9 +438,9 @@ static int l2tp_connect(struct l2tp_conn_t *conn) goto out_err; } - conn->ppp.chan_name = _strdup(inet_ntoa(conn->addr.sin_addr)); - - triton_event_fire(EV_CTRL_STARTED, &conn->ppp); + conn->ppp.ses.chan_name = _strdup(inet_ntoa(conn->addr.sin_addr)); + + triton_event_fire(EV_CTRL_STARTED, &conn->ppp.ses); if (establish_ppp(&conn->ppp)) goto out_err; @@ -682,7 +684,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))) @@ -891,7 +893,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); } |