summaryrefslogtreecommitdiff
path: root/accel-pppd/ctrl
diff options
context:
space:
mode:
Diffstat (limited to 'accel-pppd/ctrl')
-rw-r--r--accel-pppd/ctrl/ipoe/CMakeLists.txt1
-rw-r--r--accel-pppd/ctrl/ipoe/dhcpv4.c2
l---------accel-pppd/ctrl/ipoe/if_ipoe.h1
-rw-r--r--accel-pppd/ctrl/ipoe/ipoe.c242
-rw-r--r--accel-pppd/ctrl/ipoe/ipoe.h11
-rw-r--r--accel-pppd/ctrl/ipoe/ipoe_netlink.c351
6 files changed, 572 insertions, 36 deletions
diff --git a/accel-pppd/ctrl/ipoe/CMakeLists.txt b/accel-pppd/ctrl/ipoe/CMakeLists.txt
index e2b71cbc..297d81b1 100644
--- a/accel-pppd/ctrl/ipoe/CMakeLists.txt
+++ b/accel-pppd/ctrl/ipoe/CMakeLists.txt
@@ -4,6 +4,7 @@ SET(sources
ipoe.c
dhcpv4.c
dhcpv4_options.c
+ ipoe_netlink.c
)
IF (LUA)
diff --git a/accel-pppd/ctrl/ipoe/dhcpv4.c b/accel-pppd/ctrl/ipoe/dhcpv4.c
index 12923eee..78911811 100644
--- a/accel-pppd/ctrl/ipoe/dhcpv4.c
+++ b/accel-pppd/ctrl/ipoe/dhcpv4.c
@@ -109,7 +109,7 @@ struct dhcpv4_serv *dhcpv4_create(struct triton_context_t *ctx, const char *ifna
}
fcntl(raw_sock, F_SETFL, O_NONBLOCK);
- fcntl(raw_sock, F_SETFD, fcntl(sock, F_GETFD) | FD_CLOEXEC);
+ 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);
diff --git a/accel-pppd/ctrl/ipoe/if_ipoe.h b/accel-pppd/ctrl/ipoe/if_ipoe.h
new file mode 120000
index 00000000..1f0e053e
--- /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
index 1c54b7c9..eedd8cf9 100644
--- a/accel-pppd/ctrl/ipoe/ipoe.c
+++ b/accel-pppd/ctrl/ipoe/ipoe.c
@@ -8,6 +8,8 @@
#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>
@@ -49,6 +51,7 @@ static int conf_netmask = 24;
static int conf_lease_time = 600;
static int conf_lease_timeout = 660;
static int conf_verbose;
+static int conf_opt_single = 0;
static unsigned int stat_starting;
static unsigned int stat_active;
@@ -153,14 +156,37 @@ static void ipoe_session_start(struct ipoe_session *ses)
{
int r;
char *passwd;
+ struct ifreq ifr;
if (ses->serv->opt_single)
strncpy(ses->ses.ifname, ses->serv->ifname, AP_IFNAME_LEN);
+ else {
+ ses->ifindex = ipoe_nl_create(0, 0, ses->serv->ifname, 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);
+ }
- ipoe_session_set_username(ses);
if (!ses->ses.username) {
- ipoe_session_finished(&ses->ses);
- return;
+ ipoe_session_set_username(ses);
+
+ if (!ses->ses.username) {
+ ipoe_session_finished(&ses->ses);
+ return;
+ }
}
triton_event_fire(EV_CTRL_STARTING, &ses->ses);
@@ -186,44 +212,46 @@ static void ipoe_session_start(struct ipoe_session *ses)
return;
}
- if (ses->dhcpv4_request) {
- ses->ses.ipv4 = ipdb_get_ipv4(&ses->ses);
- if (!ses->ses.ipv4) {
- log_ppp_warn("no free IPv4 address\n");
- ap_session_terminate(&ses->ses, TERM_AUTH_ERROR, 0);
- return;
- }
+ 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 (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->dhcpv4_request) {
dhcpv4_send_reply(DHCPOFFER, ses->serv->dhcpv4, ses->dhcpv4_request, &ses->ses, conf_lease_time);
dhcpv4_packet_free(ses->dhcpv4_request);
ses->dhcpv4_request = NULL;
+
+ ses->timer.expire = ipoe_session_timeout;
+ ses->timer.expire_tv.tv_sec = conf_offer_timeout;
+ triton_timer_add(&ses->ctx, &ses->timer, 0);
}
-
- ses->timer.expire = ipoe_session_timeout;
- ses->timer.expire_tv.tv_sec = conf_offer_timeout;
- triton_timer_add(&ses->ctx, &ses->timer, 0);
}
static void ipoe_session_activate(struct ipoe_session *ses)
{
ap_session_activate(&ses->ses);
- if (ses->ses.state == AP_STATE_ACTIVE)
- dhcpv4_send_reply(DHCPACK, ses->serv->dhcpv4, ses->dhcpv4_request, &ses->ses, conf_lease_time);
- else
- dhcpv4_send_nak(ses->serv->dhcpv4, ses->dhcpv4_request);
+ if (ses->dhcpv4_request) {
+ if (ses->ses.state == AP_STATE_ACTIVE)
+ dhcpv4_send_reply(DHCPACK, ses->serv->dhcpv4, ses->dhcpv4_request, &ses->ses, conf_lease_time);
+ else
+ dhcpv4_send_nak(ses->serv->dhcpv4, ses->dhcpv4_request);
- dhcpv4_packet_free(ses->dhcpv4_request);
- ses->dhcpv4_request = NULL;
+ dhcpv4_packet_free(ses->dhcpv4_request);
+ ses->dhcpv4_request = NULL;
+ }
}
static void ipoe_session_keepalive(struct ipoe_session *ses)
@@ -262,10 +290,19 @@ static void ipoe_session_free(struct ipoe_session *ses)
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)
+ ipoe_nl_delete(ses->ifindex);
mempool_free(ses);
}
@@ -299,7 +336,7 @@ static void ipoe_session_close(struct triton_context_t *ctx)
ipoe_session_finished(&ses->ses);
}
-static struct ipoe_session *ipoe_session_create(struct ipoe_serv *serv, struct dhcpv4_packet *pack)
+static struct ipoe_session *ipoe_session_create_dhcpv4(struct ipoe_serv *serv, struct dhcpv4_packet *pack)
{
struct ipoe_session *ses;
int dlen = 0;
@@ -316,6 +353,7 @@ static struct ipoe_session *ipoe_session_create(struct ipoe_serv *serv, struct d
ap_session_init(&ses->ses);
ses->serv = serv;
+ ses->ifindex = -1;
ses->dhcpv4_request = pack;
ses->xid = pack->hdr->xid;
@@ -372,7 +410,7 @@ static struct ipoe_session *ipoe_session_create(struct ipoe_serv *serv, struct d
ses->ctrl.name = "ipoe";
ses->ctrl.calling_station_id = _malloc(19);
- ses->ctrl.called_station_id = serv->ifname;
+ ses->ctrl.called_station_id = _strdup(serv->ifname);
ptr = ses->hwaddr;
sprintf(ses->ctrl.calling_station_id, "%02x:%02x:%02x:%02x:%02x:%02x",
@@ -394,7 +432,7 @@ static struct ipoe_session *ipoe_session_create(struct ipoe_serv *serv, struct d
return ses;
}
-static void ipoe_dhcpv4_recv(struct dhcpv4_serv *dhcpv4, struct dhcpv4_packet *pack)
+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;
@@ -404,7 +442,7 @@ static void ipoe_dhcpv4_recv(struct dhcpv4_serv *dhcpv4, struct dhcpv4_packet *p
if (pack->msg_type == DHCPDISCOVER) {
ses = ipoe_session_lookup(serv, pack);
if (!ses) {
- ses = ipoe_session_create(serv, pack);
+ ses = ipoe_session_create_dhcpv4(serv, pack);
if (conf_verbose && ses) {
log_switch(dhcpv4->ctx, &ses->ses);
@@ -487,6 +525,80 @@ static void ipoe_dhcpv4_recv(struct dhcpv4_serv *dhcpv4, struct dhcpv4_packet *p
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;
+
+ 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->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;
+}
+
+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;
+
+ pthread_mutex_lock(&serv->lock);
+ list_for_each_entry(ses, &serv->sessions, entry) {
+ if (memcmp(ses->hwaddr, eth->h_source, 6) == 0) {
+ pthread_mutex_unlock(&serv->lock);
+ return;
+ }
+ }
+ pthread_mutex_unlock(&serv->lock);
+
+ ipoe_session_create_up(serv, eth, iph);
+ }
+}
+
static void ipoe_serv_close(struct triton_context_t *ctx)
{
struct ipoe_serv *serv = container_of(ctx, typeof(*serv), ctx);
@@ -546,9 +658,15 @@ static void add_interface(const char *ifname, int ifindex, const char *opt)
if (ptr[7] && ptr[7] != ',')
goto out_err_parse;
opt_single = 1;
- } else
- opt_single = 0;
-
+ } else {
+ ptr = strstr(opt, ",shared");
+ if (ptr) {
+ if (ptr[7] && ptr[7] != ',')
+ goto out_err_parse;
+ opt_single = 0;
+ } else
+ opt_single = conf_opt_single;
+ }
list_for_each_entry(serv, &serv_list, entry) {
if (strcmp(ifname, serv->ifname) == 0) {
@@ -567,6 +685,7 @@ static void add_interface(const char *ifname, int ifindex, const char *opt)
serv->ifindex = ifindex;
serv->opt_single = opt_single;
serv->opt_dhcpv4 = conf_dhcpv4;
+ serv->active = 1;
INIT_LIST_HEAD(&serv->sessions);
pthread_mutex_init(&serv->lock, NULL);
@@ -575,11 +694,13 @@ static void add_interface(const char *ifname, int ifindex, const char *opt)
if (serv->opt_dhcpv4) {
serv->dhcpv4 = dhcpv4_create(&serv->ctx, serv->ifname);
if (serv->dhcpv4)
- serv->dhcpv4->recv = ipoe_dhcpv4_recv;
+ serv->dhcpv4->recv = ipoe_recv_dhcpv4;
}
triton_context_wakeup(&serv->ctx);
+ list_add_tail(&serv->entry, &serv_list);
+
return;
out_err_parse:
@@ -678,6 +799,56 @@ static void load_interfaces(struct conf_sect_t *sect)
}
}
+static void parse_local_net(const char *opt)
+{
+ const char *ptr;
+ char str[17];
+ in_addr_t addr;
+ int mask;
+ char *endptr;
+
+ ptr = strchr(opt, '/');
+ if (ptr) {
+ memcpy(str, opt, ptr - opt);
+ str[ptr - opt] = 0;
+ addr = inet_addr(str);
+ if (addr == INADDR_NONE)
+ goto out_err;
+ mask = strtoul(ptr + 1, &endptr, 10);
+ if (mask > 32)
+ goto out_err;
+ } else {
+ addr = inet_addr(opt);
+ if (addr == INADDR_NONE)
+ goto out_err;
+ mask = 24;
+ }
+
+ mask = (1 << mask) - 1;
+
+ ipoe_nl_add_net(addr & mask, mask);
+
+ return;
+
+out_err:
+ log_error("ipoe: failed to parse 'local-net=%s'\n", opt);
+}
+
+static void load_local_nets(struct conf_sect_t *sect)
+{
+ struct conf_option_t *opt;
+
+ ipoe_nl_delete_nets();
+
+ list_for_each_entry(opt, &sect->items, entry) {
+ if (strcmp(opt->name, "local-net"))
+ continue;
+ if (!opt->val)
+ continue;
+ parse_local_net(opt->val);
+ }
+}
+
static void load_config(void)
{
const char *opt;
@@ -687,6 +858,7 @@ static void load_config(void)
return;
load_interfaces(s);
+ load_local_nets(s);
opt = conf_get_opt("ipoe", "username");
if (opt) {
diff --git a/accel-pppd/ctrl/ipoe/ipoe.h b/accel-pppd/ctrl/ipoe/ipoe.h
index adbc5cff..30760906 100644
--- a/accel-pppd/ctrl/ipoe/ipoe.h
+++ b/accel-pppd/ctrl/ipoe/ipoe.h
@@ -44,11 +44,22 @@ struct ipoe_session
uint32_t giaddr;
uint8_t *data;
struct dhcpv4_packet *dhcpv4_request;
+ int ifindex;
};
#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);
+
+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);
+
#endif
diff --git a/accel-pppd/ctrl/ipoe/ipoe_netlink.c b/accel-pppd/ctrl/ipoe/ipoe_netlink.c
new file mode 100644
index 00000000..57da0def
--- /dev/null
+++ b/accel-pppd/ctrl/ipoe/ipoe_netlink.c
@@ -0,0 +1,351 @@
+#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"
+
+#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_DELETE;
+
+ 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;
+}
+
+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)
+{
+ rtnl_close(&rth);
+ 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);