Skip to content
Open
1 change: 1 addition & 0 deletions accel-pppd/ctrl/ipoe/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ SET(sources
dhcpv4.c
dhcpv4_options.c
ipoe_netlink.c
ipoe_netlink_link_monitor.c
# backup.c
arp.c
)
Expand Down
123 changes: 123 additions & 0 deletions accel-pppd/ctrl/ipoe/ipoe.c
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@
#include "vlan_mon.h"

#include "ipoe.h"
#include "if_ipoe.h"

#include "memdebug.h"

Expand Down Expand Up @@ -217,6 +218,7 @@ static void ipoe_ses_recv_dhcpv4(struct dhcpv4_serv *dhcpv4, struct dhcpv4_packe
static void __ipoe_recv_dhcpv4(struct dhcpv4_serv *dhcpv4, struct dhcpv4_packet *pack, int force);
static void ipoe_session_keepalive(struct dhcpv4_packet *pack);
static void add_interface(const char *ifname, int ifindex, const char *opt, int parent_ifindex, int vid, int vlan_mon);
static void del_interface(const char *ifname, struct ipoe_serv *serv);
static int get_offer_delay();
static void __ipoe_session_start(struct ipoe_session *ses);
static int ipoe_rad_send_auth_request(struct rad_plugin_t *rad, struct rad_packet_t *pack);
Expand All @@ -226,6 +228,7 @@ static void ipoe_serv_timeout(struct triton_timer_t *t);
static struct ipoe_session *ipoe_session_create_up(struct ipoe_serv *serv, struct ethhdr *eth, struct iphdr *iph, struct _arphdr *arph);
static void __terminate(struct ap_session *ses);
static void ipoe_ipv6_disable(struct ipoe_serv *serv);
static struct conf_option_t *ipoe_find_opt(const char *name);

static void ipoe_ctx_switch(struct triton_context_t *ctx, void *arg)
{
Expand Down Expand Up @@ -2766,6 +2769,62 @@ struct ipoe_serv *ipoe_find_serv(const char *ifname)
return NULL;
}

static struct conf_option_t *ipoe_find_opt(const char *ifname)
{
struct conf_sect_t *sect = conf_get_section("ipoe");
struct conf_option_t *opt;
const char *pcre_err;
struct ifreq ifr;
int pcre_offset;
const char *ptr;
pcre *re = NULL;
char *pattern;

list_for_each_entry(opt, &sect->items, entry) {
if (strcmp(opt->name, "interface"))
continue;
if (!opt->val)
continue;

for (ptr = opt->val; *ptr && *ptr != ','; ptr++);

if (strlen(opt->val) > 3 && memcmp(opt->val, "re:", 3) == 0) {
pattern = _malloc(ptr - (opt->val + 3) + 1);
memcpy(pattern, opt->val + 3, ptr - (opt->val + 3));
pattern[ptr - (opt->val + 3)] = 0;

re = pcre_compile2(pattern, 0, NULL, &pcre_err, &pcre_offset, NULL);

_free(pattern);

if (!re) {
log_error("ipoe: '%s': %s at %i\r\n", pattern, pcre_err, pcre_offset);
pcre_free(re);
continue;
}

if (pcre_exec(re, NULL, ifname, strlen(ifname), 0, 0, NULL, 0) < 0) {
pcre_free(re);
continue;
}

pcre_free(re);
} else {
if (ptr - opt->val >= sizeof(ifr.ifr_name))
continue;

memcpy(ifr.ifr_name, opt->val, ptr - opt->val);
ifr.ifr_name[ptr - opt->val] = 0;

if (strcmp(ifr.ifr_name, ifname) != 0)
continue;
}
return opt;
}

return NULL;
}

static int get_offer_delay()
{
struct delay *r, *prev = NULL;
Expand Down Expand Up @@ -2793,6 +2852,52 @@ static void set_vlan_timeout(struct ipoe_serv *serv)
triton_timer_add(&serv->ctx, &serv->timer, 0);
}

void ipoe_netlink_mon_notify(int ifindex, const char *ifname, bool add)
{
struct conf_option_t *opt;
struct ipoe_serv *serv;
int vid, parent_ifindex;

opt = ipoe_find_opt(ifname);
if (!opt)
return;

serv = ipoe_find_serv(ifname);

if (add) {
if (serv && serv->active) {
/* interface is already loaded */
if (serv->ifindex == ifindex)
/* no change */
return;
else
/* unload previous interface */
del_interface(ifname, serv);
}

if (serv) {
/* VLAN information was already registered last time add_interface()
* was called. They cannot change unless the interface is deleted
* and re-created with the same name but with different VLAN info.
* In that case, del_interface() would be called and then
* add_interface()
*/
vid = 0;
parent_ifindex = 0;
} else
vid = iplink_vlan_get_vid(ifindex, &parent_ifindex);

add_interface(ifname, ifindex, opt->val, parent_ifindex, vid, 0);
} else {
if (!serv || !serv->active)
/* interface is not loaded */
return;

/* unload interface */
del_interface(ifname, serv);
}
}

void ipoe_vlan_mon_notify(int ifindex, int vid, int vlan_ifindex)
{
struct conf_sect_t *sect = conf_get_section("ipoe");
Expand Down Expand Up @@ -3312,6 +3417,18 @@ static void add_interface(const char *ifname, int ifindex, const char *opt, int
_free(str0);
}

static void del_interface(const char *ifname, struct ipoe_serv *serv)
{
pthread_mutex_lock(&serv->lock);

serv->active = 0;
ipoe_drop_sessions(serv, NULL);
serv->need_close = 1;
triton_context_call(&serv->ctx, (triton_event_func)ipoe_serv_release, serv);

pthread_mutex_unlock(&serv->lock);
}

static void load_interface(const char *opt)
{
const char *ptr;
Expand Down Expand Up @@ -3401,6 +3518,12 @@ static void load_interfaces(struct conf_sect_t *sect)
{
struct ipoe_serv *serv;
struct conf_option_t *opt;
static int ipoe_netlink_mon_registered = 0;

if (!ipoe_netlink_mon_registered) {
ipoe_netlink_mon_register(ipoe_netlink_mon_notify);
ipoe_netlink_mon_registered = 1;
}

list_for_each_entry(serv, &serv_list, entry)
serv->active = 0;
Expand Down
3 changes: 3 additions & 0 deletions accel-pppd/ctrl/ipoe/ipoe.h
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
#include <stdint.h>
#include <pthread.h>
#include <linux/if.h>
#include <stdbool.h>

#include "triton.h"
#include "ap_session.h"
Expand Down Expand Up @@ -164,5 +165,7 @@ void arp_send(int ifindex, struct _arphdr *arph, int bc);

int ipoe_check_localnet(in_addr_t addr);

void ipoe_netlink_mon_notify(int ifindex, const char *ifname, bool add);

#endif

86 changes: 13 additions & 73 deletions accel-pppd/ctrl/ipoe/ipoe_netlink.c
Original file line number Diff line number Diff line change
Expand Up @@ -518,82 +518,22 @@ static void ipoe_up_handler(const struct sockaddr_nl *addr, struct nlmsghdr *h)
}
}

static int ipoe_mc_read(struct triton_md_handler_t *h)
static int ipoe_mc_read_handler(const struct sockaddr_nl *nladdr,
struct nlmsghdr *hdr, void *arg)
{
int status;
struct nlmsghdr *hdr;
struct genlmsghdr *ghdr;
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;
}

ghdr = NLMSG_DATA(hdr);

if (ghdr->cmd == IPOE_REP_PKT)
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;
}
}
ghdr = NLMSG_DATA(hdr);

if (ghdr->cmd == IPOE_REP_PKT)
ipoe_up_handler(nladdr, hdr);

return 0;
}

static int ipoe_mc_read(struct triton_md_handler_t *h)
{
rtnl_listen(&rth, ipoe_mc_read_handler, NULL);

return 0;
}
Expand Down
97 changes: 97 additions & 0 deletions accel-pppd/ctrl/ipoe/ipoe_netlink_link_monitor.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
#include <unistd.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <pthread.h>
#include <fcntl.h>
#include <linux/genetlink.h>

#include "triton.h"
#include "log.h"
#include "libnetlink.h"

#include "ipoe.h"
#include "if_ipoe.h"

static struct rtnl_handle rth;
static struct triton_md_handler_t mc_hnd;
static interface_mon_notify cb = NULL;

void __export ipoe_netlink_mon_register(interface_mon_notify func)
{
cb = func;
}

static int ipoe_mc_read_handler(const struct sockaddr_nl *nladdr,
struct nlmsghdr *hdr, void *arg)
{
struct rtattr *tb[IFLA_MAX+1];
struct ifinfomsg *ifi;
char ifname[IFNAMSIZ] = {0};

ifi = NLMSG_DATA(hdr);

parse_rtattr(tb, IFLA_MAX, IFLA_RTA(ifi),
hdr->nlmsg_len - NLMSG_LENGTH(sizeof(struct ifinfomsg)));

if (tb[IFLA_IFNAME] && strlen(RTA_DATA(tb[IFLA_IFNAME])) < IFNAMSIZ)
strncpy(ifname, RTA_DATA(tb[IFLA_IFNAME]), IFNAMSIZ);

log_debug("ipoe: netlink message RTM_%sLINK for interface %s index %d\n",
hdr->nlmsg_type == RTM_NEWLINK ? "NEW" : "DEL",
strlen(ifname) ? ifname : "<unknown>", ifi->ifi_index);

if (cb && strlen(ifname))
/* notify ipoe of the event using the callback function */
cb(ifi->ifi_index, ifname, hdr->nlmsg_type == RTM_NEWLINK);

return 0;
}

static int ipoe_mc_read(struct triton_md_handler_t *h)
{
rtnl_listen(&rth, ipoe_mc_read_handler, NULL);

return 0;
}

static void ipoe_mc_close(struct triton_context_t *ctx)
{
triton_md_unregister_handler(&mc_hnd, 0);
triton_context_unregister(ctx);
}

static void ipoe_mc_ctx_switch(struct triton_context_t *ctx, void *arg)
{
net = def_net;
log_switch(NULL, NULL);
}

static struct triton_context_t mc_ctx = {
.close = ipoe_mc_close,
.before_switch = ipoe_mc_ctx_switch,
};

static struct triton_md_handler_t mc_hnd = {
.read = ipoe_mc_read,
};

static void init(void)
{
if (rtnl_open_byproto(&rth, 1 << (RTNLGRP_LINK - 1), NETLINK_ROUTE)) {
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(&mc_ctx, NULL);
mc_hnd.fd = rth.fd;
triton_md_register_handler(&mc_ctx, &mc_hnd);
triton_md_enable_handler(&mc_hnd, MD_MODE_READ);
triton_context_wakeup(&mc_ctx);
}

DEFINE_INIT(18, init);
Loading