Sign Up
Log In
Log In
or
Sign Up
Places
All Projects
Status Monitor
Collapse sidebar
openSUSE:Leap:42.2:Update
kdump
kdump-netcheck-0001-routable-class.patch
Overview
Repositories
Revisions
Requests
Users
Attributes
Meta
File kdump-netcheck-0001-routable-class.patch of Package kdump
Date: Fri Oct 9 21:32:10 2015 +0200 From: Petr Tesarik <ptesarik@suse.com> Subject: Add Routable class References: bsc#944201 Patch-mainline: v0.8.16 Git-commit: 8b5e2ba1639945f7c88e0ecddcfd2b895be68ce4 This class can be used to check whether a target is routable or not. It uses a netlink socket to get routing information from the kernel and can wait for changes in routing. Signed-off-by: Petr Tesarik <ptesarik@suse.com> --- kdumptool/CMakeLists.txt | 2 kdumptool/routable.cc | 410 +++++++++++++++++++++++++++++++++++++++++++++++ kdumptool/routable.h | 61 ++++++ 3 files changed, 473 insertions(+) --- a/kdumptool/CMakeLists.txt +++ b/kdumptool/CMakeLists.txt @@ -90,6 +90,8 @@ SET(COMMON_SRC multipath.h calibrate.cc calibrate.h + routable.cc + routable.h ) add_library(common STATIC ${COMMON_SRC}) --- /dev/null +++ b/kdumptool/routable.cc @@ -0,0 +1,410 @@ +/* + * (c) 2015, Petr Tesarik <ptesarik@suse.com>, SUSE LINUX GmbH + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +#include <string.h> + +#include <unistd.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <asm/types.h> +#include <linux/netlink.h> +#include <linux/rtnetlink.h> +#include <arpa/inet.h> +#include <netdb.h> +#include <errno.h> + +#include "global.h" +#include "routable.h" +#include "debug.h" + +//{{{ NetLink ------------------------------------------------------------------ + +#define NETLINK_DEF_RECV_MAX 1024 + +class NetLink { + public: + NetLink(unsigned subscribe = 0U, + size_t recv_max = NETLINK_DEF_RECV_MAX); + + ~NetLink(); + + int checkRoute(const struct addrinfo *ai); + + int waitRouteChange(void); + + protected: + class RecvCheck { + public: + virtual bool check(const struct sockaddr_nl *nladdr, + const struct nlmsghdr *nh) const = 0; + }; + class RouteRecvCheck : public RecvCheck { + public: + virtual bool check(const struct sockaddr_nl *nladdr, + const struct nlmsghdr *nh) const; + }; + class ReplyRecvCheck : public RecvCheck { + public: + ReplyRecvCheck(unsigned peer, unsigned pid, unsigned seq) + : m_peer(peer), m_pid(pid), m_seq(seq) + {} + + virtual bool check(const struct sockaddr_nl *nladdr, + const struct nlmsghdr *nh) const; + private: + unsigned m_peer, m_pid, m_seq; + }; + + int receive(const RecvCheck &rc); + + int talk(struct nlmsghdr *req, unsigned peer, unsigned groups); + + struct nlmsghdr *message(void) const + throw () + { return m_message; } + + private: + int m_fd; + struct sockaddr_nl m_local; + static unsigned m_seq; + + size_t m_buflen; + unsigned char *m_buffer; + struct nlmsghdr *m_message; +}; + +unsigned NetLink::m_seq; + +// ----------------------------------------------------------------------------- +NetLink::NetLink(unsigned subscribe, size_t recv_max) + : m_buflen(recv_max) +{ + struct sockaddr_nl sa; + socklen_t salen; + + m_buffer = new unsigned char[m_buflen]; + m_fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE); + if (m_fd < 0) + throw KSystemError("Cannot create netlink", errno); + + memset(&sa, 0, sizeof(sa)); + sa.nl_family = AF_NETLINK; + sa.nl_groups = subscribe; + if (bind(m_fd, (struct sockaddr *) &sa, sizeof sa) != 0) + throw KSystemError("Cannot bind netlink", errno); + + salen = sizeof m_local; + if (getsockname(m_fd, (struct sockaddr*)&m_local, &salen) < 0) + throw KSystemError("Cannot get local netlink address", errno); + if (salen != sizeof m_local || m_local.nl_family != AF_NETLINK) + throw KError("Invalid local netlink address"); +} + +// ----------------------------------------------------------------------------- +NetLink::~NetLink() +{ + if (m_fd >= 0) + close(m_fd); + + delete[] m_buffer; +} + +// ----------------------------------------------------------------------------- +bool NetLink::RouteRecvCheck::check(const struct sockaddr_nl *nladdr, + const struct nlmsghdr *nh) const +{ + Debug::debug()->trace("RouteRecvCheck::check(%u)", + (unsigned)nh->nlmsg_type); + + if (nh->nlmsg_type != RTM_NEWROUTE && + nh->nlmsg_type != RTM_DELROUTE) + return false; + + size_t rtlen = NLMSG_PAYLOAD(nh, 0); + if (rtlen < sizeof(struct rtmsg)) + throw KError("Netlink rtmsg truncated"); + + struct rtmsg *rt = (struct rtmsg*)NLMSG_DATA(nh); + if (rt->rtm_flags & RTM_F_CLONED) + return false; + + return true; +} + +// ----------------------------------------------------------------------------- +bool NetLink::ReplyRecvCheck::check(const struct sockaddr_nl *nladdr, + const struct nlmsghdr *nh) const +{ + return (nladdr->nl_pid == m_peer && + nh->nlmsg_pid == m_pid && + nh->nlmsg_seq == m_seq); +} + +// ----------------------------------------------------------------------------- +int NetLink::receive(const RecvCheck &rc) +{ + struct sockaddr_nl nladdr; + struct iovec iov; + struct msghdr msg; + + m_message = NULL; + + msg.msg_name = &nladdr; + msg.msg_namelen = sizeof nladdr; + msg.msg_iov = &iov; + msg.msg_iovlen = 1; + + while (1) { + struct nlmsghdr *nh; + ssize_t len; + + iov.iov_base = m_buffer; + iov.iov_len = m_buflen; + len = recvmsg(m_fd, &msg, 0); + if (len < 0) + throw KSystemError("Cannot receive netlink message", errno); + if (!len) + throw KError("EOF on netlink receive"); + if (msg.msg_namelen != sizeof nladdr) + throw KError("Invalid netlink sender address length"); + + for (nh = (struct nlmsghdr*)m_buffer; NLMSG_OK(nh, len); + nh = NLMSG_NEXT(nh, len)) { + if (rc.check(&nladdr, nh)) { + m_message = nh; + + if (nh->nlmsg_type != NLMSG_ERROR) + return 0; + + size_t datalen = NLMSG_PAYLOAD(nh, 0); + if (datalen < sizeof(struct nlmsgerr)) + throw KError("Netlink ERROR reply truncated"); + + struct nlmsgerr *err = (struct nlmsgerr*)NLMSG_DATA(nh); + return err->error; + } + } + + if (msg.msg_flags & MSG_TRUNC) + throw KError("Netlink message truncated"); + + if (len) + throw KError("Invalid netlink message length"); + } +} + +// ----------------------------------------------------------------------------- +int NetLink::waitRouteChange(void) +{ + Debug::debug()->trace("waitRouteChange()"); + + RouteRecvCheck rc; + return receive(rc); +} + +// ----------------------------------------------------------------------------- +int NetLink::talk(struct nlmsghdr *req, unsigned peer, unsigned groups) +{ + struct sockaddr_nl nladdr; + struct iovec iov; + struct msghdr msg; + unsigned seq; + ssize_t len; + + memset(&nladdr, 0, sizeof nladdr); + nladdr.nl_family = AF_NETLINK; + nladdr.nl_pid = peer; + nladdr.nl_groups = groups; + + memset(&iov, 0, sizeof iov); + iov.iov_base = (void*)req; + iov.iov_len = req->nlmsg_len; + + memset(&msg, 0, sizeof msg); + msg.msg_name = &nladdr; + msg.msg_namelen = sizeof nladdr; + msg.msg_iov = &iov; + msg.msg_iovlen = 1; + + req->nlmsg_seq = seq = ++m_seq; + + len = sendmsg(m_fd, &msg, 0); + if (len < 0) + throw KSystemError("Cannot send netlink message", errno); + + ReplyRecvCheck rc(peer, m_local.nl_pid, seq); + return receive(rc); +} + +// ----------------------------------------------------------------------------- +int NetLink::checkRoute(const struct addrinfo *ai) +{ + size_t addrlen; + struct { + struct nlmsghdr nh; + struct rtmsg rt; + struct rtattr rta; + union { + struct in_addr in_addr; + struct in6_addr in6_addr; + } u; + } req; + int res; + + if (Debug::debug()->isDebugEnabled()) { + char ip[INET6_ADDRSTRLEN]; + getnameinfo(ai->ai_addr, ai->ai_addrlen, ip, sizeof ip, + NULL, 0, NI_NUMERICHOST); + Debug::debug()->trace("checkRoute(%s)", ip); + } + + memset(&req, 0, sizeof req); + + if (ai->ai_family == AF_INET) { + const struct sockaddr_in *sin = + (struct sockaddr_in *)ai->ai_addr; + + req.u.in_addr = sin->sin_addr; + addrlen = sizeof(struct in_addr); + } else { + const struct sockaddr_in6 *sin6 = + (struct sockaddr_in6 *)ai->ai_addr; + + req.u.in6_addr = sin6->sin6_addr; + addrlen = sizeof(struct in6_addr); + } + + req.rta.rta_type = RTA_DST; + req.rta.rta_len = RTA_LENGTH(addrlen); + req.rt.rtm_family = ai->ai_family; + req.rt.rtm_dst_len = addrlen * 8; + req.nh.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg) + req.rta.rta_len); + req.nh.nlmsg_type = RTM_GETROUTE; + req.nh.nlmsg_flags = NLM_F_REQUEST; + + if ( (res = talk(&req.nh, 0, 0)) < 0) + return res; + + struct nlmsghdr *nh = message(); + if (nh->nlmsg_type != RTM_NEWROUTE) + throw KError("Netlink RTM_GETROUTE reply mismatch"); + + size_t rtlen = NLMSG_PAYLOAD(nh, 0); + if (rtlen < sizeof(struct rtmsg)) + throw KError("Netlink rtmsg truncated"); + + struct rtmsg *rt = (struct rtmsg*)NLMSG_DATA(nh); + switch (rt->rtm_type) { + case RTN_UNSPEC: + case RTN_UNICAST: + case RTN_LOCAL: + case RTN_BROADCAST: + case RTN_ANYCAST: + case RTN_MULTICAST: + return 0; + case RTN_UNREACHABLE: + return -EHOSTUNREACH; + case RTN_BLACKHOLE: + return -EINVAL; + case RTN_PROHIBIT: + return -EACCES; + case RTN_THROW: + return -EAGAIN; + default: + return -EINVAL; + } +} + +//}}} + +//{{{ Routable ----------------------------------------------------------------- + +// ----------------------------------------------------------------------------- +Routable::~Routable() +{ + if (m_ai) + freeaddrinfo(m_ai); +} + +// ----------------------------------------------------------------------------- +bool Routable::hasRoute(void) +{ + NetLink nl; + struct addrinfo *p; + + Debug::debug()->trace("hasRoute(%s)", m_host.c_str()); + + for (p = m_ai; p; p = p->ai_next) { + if (nl.checkRoute(p) == 0) + return true; + } + + return false; +} + +// ----------------------------------------------------------------------------- +bool Routable::resolve(void) + throw (KError) +{ + struct addrinfo hints; + int res; + + Debug::debug()->trace("resolve(%s)", m_host.c_str()); + + if (m_ai) + freeaddrinfo(m_ai); + + memset(&hints, 0, sizeof hints); + hints.ai_family = AF_UNSPEC; + hints.ai_socktype = SOCK_RAW; + do { + res = getaddrinfo(m_host.c_str(), NULL, &hints, &m_ai); + } while (res == EAI_AGAIN); + + if (res == 0) + return true; + + if (res == EAI_SYSTEM) + throw KSystemError("Name resolution failed", errno); + + if (res != EAI_NONAME && res != EAI_FAIL && res != EAI_NODATA) + throw KGaiError("Name resolution failed", res); + + return false; +} + +// ----------------------------------------------------------------------------- +bool Routable::check(void) +{ + NetLink nl(RTMGRP_IPV4_ROUTE | RTMGRP_IPV6_ROUTE); + + while (!resolve()) + if (nl.waitRouteChange() != 0) + return false; + + while (!hasRoute()) + if (nl.waitRouteChange() != 0) + return false; + + return true; +} + +//}}} + +// vim: set sw=4 ts=4 fdm=marker et: :collapseFolds=1: --- /dev/null +++ b/kdumptool/routable.h @@ -0,0 +1,61 @@ +/* + * (c) 2015, Petr Tesarik <ptesarik@suse.com>, SUSE LINUX GmbH + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ +#ifndef ROUTABLE_H +#define ROUTABLE_H + +#include <string> + +#include "subcommand.h" +#include "global.h" + +//{{{ Routable ----------------------------------------------------------------- + +struct addrinfo; + +/** + * Remote target that can be checked for routability. + */ +class Routable { + + public: + Routable(const std::string &host) + : m_host(host), m_ai(NULL) + {} + + ~Routable(); + + bool check(void); + + protected: + bool resolve(void) + throw (KError); + + bool hasRoute(void); + + private: + int m_nlfd; + std::string m_host; + struct addrinfo *m_ai; +}; + +//}}} + +#endif /* ROUTABLE_H */ + +// vim: set sw=4 ts=4 fdm=marker et: :collapseFolds=1:
Locations
Projects
Search
Status Monitor
Help
OpenBuildService.org
Documentation
API Documentation
Code of Conduct
Contact
Support
@OBShq
Terms
openSUSE Build Service is sponsored by
The Open Build Service is an
openSUSE project
.
Sign Up
Log In
Places
Places
All Projects
Status Monitor