diff options
Diffstat (limited to 'accel-pppd/ctrl/ipoe')
-rw-r--r-- | accel-pppd/ctrl/ipoe/CMakeLists.txt | 1 | ||||
-rw-r--r-- | accel-pppd/ctrl/ipoe/dhcpv4.c | 2 | ||||
l--------- | accel-pppd/ctrl/ipoe/if_ipoe.h | 1 | ||||
-rw-r--r-- | accel-pppd/ctrl/ipoe/ipoe.c | 242 | ||||
-rw-r--r-- | accel-pppd/ctrl/ipoe/ipoe.h | 11 | ||||
-rw-r--r-- | accel-pppd/ctrl/ipoe/ipoe_netlink.c | 351 |
6 files changed, 572 insertions, 36 deletions
diff --git a/accel-pppd/ctrl/ipoe/CMakeLists.txt b/accel-pppd/ctrl/ipoe/CMakeLists.txt index e2b71cb..297d81b 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 12923ee..7891181 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 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 index 1c54b7c..eedd8cf 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, §->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 adbc5cf..3076090 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 0000000..57da0de --- /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); |