diff --git a/accel-pppd/ctrl/ipoe/CMakeLists.txt b/accel-pppd/ctrl/ipoe/CMakeLists.txt index 8ab5975a6..1e8477b3f 100644 --- a/accel-pppd/ctrl/ipoe/CMakeLists.txt +++ b/accel-pppd/ctrl/ipoe/CMakeLists.txt @@ -5,6 +5,7 @@ SET(sources dhcpv4.c dhcpv4_options.c ipoe_netlink.c + ipoe_netlink_link_monitor.c # backup.c arp.c ) diff --git a/accel-pppd/ctrl/ipoe/ipoe.c b/accel-pppd/ctrl/ipoe/ipoe.c index dde3f213d..51ae90e89 100644 --- a/accel-pppd/ctrl/ipoe/ipoe.c +++ b/accel-pppd/ctrl/ipoe/ipoe.c @@ -39,6 +39,7 @@ #include "vlan_mon.h" #include "ipoe.h" +#include "if_ipoe.h" #include "memdebug.h" @@ -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); @@ -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) { @@ -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, §->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; @@ -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"); @@ -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; @@ -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; diff --git a/accel-pppd/ctrl/ipoe/ipoe.h b/accel-pppd/ctrl/ipoe/ipoe.h index 07dce1e2d..44aeb8597 100644 --- a/accel-pppd/ctrl/ipoe/ipoe.h +++ b/accel-pppd/ctrl/ipoe/ipoe.h @@ -4,6 +4,7 @@ #include #include #include +#include #include "triton.h" #include "ap_session.h" @@ -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 diff --git a/accel-pppd/ctrl/ipoe/ipoe_netlink.c b/accel-pppd/ctrl/ipoe/ipoe_netlink.c index e7080e92a..2858f9d4d 100644 --- a/accel-pppd/ctrl/ipoe/ipoe_netlink.c +++ b/accel-pppd/ctrl/ipoe/ipoe_netlink.c @@ -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; } diff --git a/accel-pppd/ctrl/ipoe/ipoe_netlink_link_monitor.c b/accel-pppd/ctrl/ipoe/ipoe_netlink_link_monitor.c new file mode 100644 index 000000000..b30ecb74f --- /dev/null +++ b/accel-pppd/ctrl/ipoe/ipoe_netlink_link_monitor.c @@ -0,0 +1,97 @@ +#include +#include +#include +#include +#include +#include +#include + +#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 : "", 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); diff --git a/accel-pppd/libnetlink/libnetlink.c b/accel-pppd/libnetlink/libnetlink.c index a0ea103e3..1152a2545 100644 --- a/accel-pppd/libnetlink/libnetlink.c +++ b/accel-pppd/libnetlink/libnetlink.c @@ -433,7 +433,7 @@ int __export rtnl_listen(struct rtnl_handle *rtnl, .msg_iov = &iov, .msg_iovlen = 1, }; - char buf[8192]; + char buf[MAX_MSG]; memset(&nladdr, 0, sizeof(nladdr)); nladdr.nl_family = AF_NETLINK; @@ -447,7 +447,7 @@ int __export rtnl_listen(struct rtnl_handle *rtnl, if (status < 0) { if (errno == EINTR || errno == EAGAIN) - continue; + break; log_debug("libnetlink: ""netlink receive error %s (%d)\n", strerror(errno), errno); if (errno == ENOBUFS) @@ -460,7 +460,7 @@ int __export rtnl_listen(struct rtnl_handle *rtnl, } if (msg.msg_namelen != sizeof(nladdr)) { log_debug("libnetlink: ""Sender address length == %d\n", msg.msg_namelen); - exit(1); + return -1; } for (h = (struct nlmsghdr*)buf; status >= sizeof(*h); ) { int err; @@ -473,7 +473,7 @@ int __export rtnl_listen(struct rtnl_handle *rtnl, return -1; } log_debug("libnetlink: ""!!!malformed message: len=%d\n", len); - exit(1); + return -1; } err = handler(&nladdr, h, jarg); @@ -489,9 +489,11 @@ int __export rtnl_listen(struct rtnl_handle *rtnl, } if (status) { log_debug("libnetlink: ""!!!Remnant of size %d\n", status); - exit(1); + return -1; } } + + return 0; } int __export addattr32(struct nlmsghdr *n, int maxlen, int type, __u32 data) diff --git a/drivers/ipoe/ipoe.h b/drivers/ipoe/ipoe.h index 4097e2da3..09cdbe642 100644 --- a/drivers/ipoe/ipoe.h +++ b/drivers/ipoe/ipoe.h @@ -46,5 +46,9 @@ enum { #define IPOE_GENL_MCG_PKT "Packet" #define IPOE_GENL_VERSION 0x02 +typedef void (*interface_mon_notify)(int ifindex, const char *ifname, bool add); + +void ipoe_netlink_mon_register(interface_mon_notify func); + #endif