Changeset View
Changeset View
Standalone View
Standalone View
usr.sbin/rtsold/cap_probe.c
- This file was moved from usr.sbin/rtsold/probe.c.
Show All 28 Lines | |||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY | ||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | ||||
* SUCH DAMAGE. | * SUCH DAMAGE. | ||||
* | * | ||||
* $FreeBSD$ | * $FreeBSD$ | ||||
*/ | */ | ||||
#include <sys/param.h> | #include <sys/param.h> | ||||
#include <sys/types.h> | #include <sys/capsicum.h> | ||||
#include <sys/ioctl.h> | |||||
#include <sys/socket.h> | #include <sys/socket.h> | ||||
#include <sys/sysctl.h> | #include <sys/sysctl.h> | ||||
#include <sys/uio.h> | |||||
#include <sys/queue.h> | |||||
#include <net/if.h> | #include <net/if.h> | ||||
#include <net/if_dl.h> | #include <net/if_dl.h> | ||||
#include <netinet/in.h> | #include <netinet/in.h> | ||||
#include <netinet6/in6_var.h> | #include <netinet6/in6_var.h> | ||||
#include <netinet/icmp6.h> | #include <netinet/icmp6.h> | ||||
#include <netinet6/nd6.h> | #include <netinet6/nd6.h> | ||||
#include <arpa/inet.h> | #include <arpa/inet.h> | ||||
#include <errno.h> | #include <errno.h> | ||||
#include <unistd.h> | #include <stdlib.h> | ||||
#include <string.h> | #include <string.h> | ||||
#include <syslog.h> | #include <syslog.h> | ||||
#include <stdlib.h> | #include <unistd.h> | ||||
#include <libcasper.h> | |||||
#include <libcasper_service.h> | |||||
#include "rtsold.h" | #include "rtsold.h" | ||||
static struct msghdr sndmhdr; | static int | ||||
static struct iovec sndiov[2]; | getsocket(int *sockp) | ||||
static int probesock; | |||||
static void sendprobe(struct in6_addr *, struct ifinfo *); | |||||
int | |||||
probe_init(void) | |||||
{ | { | ||||
int scmsglen = CMSG_SPACE(sizeof(struct in6_pktinfo)) + | static int probesock = -1; | ||||
CMSG_SPACE(sizeof(int)); | cap_rights_t rights; | ||||
static u_char *sndcmsgbuf = NULL; | int error, sock; | ||||
if (sndcmsgbuf == NULL && | if (probesock >= 0) { | ||||
(sndcmsgbuf = (u_char *)malloc(scmsglen)) == NULL) { | *sockp = probesock; | ||||
warnmsg(LOG_ERR, __func__, "malloc failed"); | return (0); | ||||
return (-1); | |||||
} | } | ||||
if ((probesock = socket(AF_INET6, SOCK_RAW, IPPROTO_NONE)) < 0) { | if ((sock = socket(AF_INET6, SOCK_RAW, IPPROTO_NONE)) < 0) { | ||||
error = errno; | |||||
warnmsg(LOG_ERR, __func__, "socket: %s", strerror(errno)); | warnmsg(LOG_ERR, __func__, "socket: %s", strerror(errno)); | ||||
return (-1); | return (error); | ||||
} | } | ||||
cap_rights_init(&rights, CAP_CONNECT, CAP_SEND); | |||||
if (cap_rights_limit(sock, &rights) != 0) { | |||||
error = errno; | |||||
warnmsg(LOG_ERR, __func__, "cap_rights_limit(): %s", | |||||
strerror(errno)); | |||||
return (error); | |||||
} | |||||
*sockp = probesock = sock; | |||||
/* initialize msghdr for sending packets */ | |||||
sndmhdr.msg_namelen = sizeof(struct sockaddr_in6); | |||||
sndmhdr.msg_iov = sndiov; | |||||
sndmhdr.msg_iovlen = 1; | |||||
sndmhdr.msg_control = (caddr_t)sndcmsgbuf; | |||||
sndmhdr.msg_controllen = scmsglen; | |||||
return (0); | return (0); | ||||
} | } | ||||
/* | |||||
* Probe if each router in the default router list is still alive. | |||||
*/ | |||||
void | |||||
defrouter_probe(struct ifinfo *ifinfo) | |||||
{ | |||||
struct in6_defrouter *p, *ep; | |||||
int ifindex, mib[4]; | |||||
char *buf, ntopbuf[INET6_ADDRSTRLEN]; | |||||
size_t l; | |||||
ifindex = ifinfo->sdl->sdl_index; | |||||
if (ifindex == 0) | |||||
return; | |||||
mib[0] = CTL_NET; | |||||
mib[1] = PF_INET6; | |||||
mib[2] = IPPROTO_ICMPV6; | |||||
mib[3] = ICMPV6CTL_ND6_DRLIST; | |||||
if (sysctl(mib, nitems(mib), NULL, &l, NULL, 0) < 0) { | |||||
warnmsg(LOG_ERR, __func__, "sysctl(ICMPV6CTL_ND6_DRLIST): %s", | |||||
strerror(errno)); | |||||
return; | |||||
} | |||||
if (l == 0) | |||||
return; | |||||
buf = malloc(l); | |||||
if (buf == NULL) { | |||||
warnmsg(LOG_ERR, __func__, "malloc(): %s", strerror(errno)); | |||||
return; | |||||
} | |||||
if (sysctl(mib, nitems(mib), buf, &l, NULL, 0) < 0) { | |||||
warnmsg(LOG_ERR, __func__, "sysctl(ICMPV6CTL_ND6_DRLIST): %s", | |||||
strerror(errno)); | |||||
free(buf); | |||||
return; | |||||
} | |||||
ep = (struct in6_defrouter *)(void *)(buf + l); | |||||
for (p = (struct in6_defrouter *)(void *)buf; p < ep; p++) { | |||||
if (ifindex != p->if_index) | |||||
continue; | |||||
if (!IN6_IS_ADDR_LINKLOCAL(&p->rtaddr.sin6_addr)) { | |||||
warnmsg(LOG_ERR, __func__, | |||||
"default router list contains a " | |||||
"non-link-local address(%s)", | |||||
inet_ntop(AF_INET6, &p->rtaddr.sin6_addr, ntopbuf, | |||||
INET6_ADDRSTRLEN)); | |||||
continue; /* ignore the address */ | |||||
} | |||||
sendprobe(&p->rtaddr.sin6_addr, ifinfo); | |||||
} | |||||
free(buf); | |||||
} | |||||
static void | static void | ||||
sendprobe(struct in6_addr *addr, struct ifinfo *ifinfo) | sendprobe(int sock, struct in6_addr *addr, uint32_t ifindex, uint32_t linkid) | ||||
{ | { | ||||
uint8_t cmsg[CMSG_SPACE(sizeof(struct in6_pktinfo)) + | |||||
CMSG_SPACE(sizeof(int))]; | |||||
struct msghdr hdr; | |||||
struct iovec iov; | |||||
u_char ntopbuf[INET6_ADDRSTRLEN], ifnamebuf[IFNAMSIZ]; | u_char ntopbuf[INET6_ADDRSTRLEN], ifnamebuf[IFNAMSIZ]; | ||||
struct sockaddr_in6 sa6_probe; | struct sockaddr_in6 sa6_probe; | ||||
struct in6_pktinfo *pi; | struct in6_pktinfo *pi; | ||||
struct cmsghdr *cm; | struct cmsghdr *cm; | ||||
u_int32_t ifindex = ifinfo->sdl->sdl_index; | ssize_t n; | ||||
int hoplimit = 1; | int error, hoplimit; | ||||
memset(&sa6_probe, 0, sizeof(sa6_probe)); | memset(&sa6_probe, 0, sizeof(sa6_probe)); | ||||
sa6_probe.sin6_family = AF_INET6; | sa6_probe.sin6_family = AF_INET6; | ||||
sa6_probe.sin6_len = sizeof(sa6_probe); | sa6_probe.sin6_len = sizeof(sa6_probe); | ||||
sa6_probe.sin6_addr = *addr; | sa6_probe.sin6_addr = *addr; | ||||
sa6_probe.sin6_scope_id = ifinfo->linkid; | sa6_probe.sin6_scope_id = linkid; | ||||
sndmhdr.msg_name = (caddr_t)&sa6_probe; | memset(&hdr, 0, sizeof(hdr)); | ||||
sndmhdr.msg_iov[0].iov_base = NULL; | hdr.msg_name = (caddr_t)&sa6_probe; | ||||
sndmhdr.msg_iov[0].iov_len = 0; | hdr.msg_namelen = sizeof(sa6_probe); | ||||
hdr.msg_iov = &iov; | |||||
hdr.msg_iovlen = 1; | |||||
hdr.msg_control = cmsg; | |||||
hdr.msg_controllen = sizeof(cmsg); | |||||
cm = CMSG_FIRSTHDR(&sndmhdr); | iov.iov_base = NULL; | ||||
/* specify the outgoing interface */ | iov.iov_len = 0; | ||||
/* Specify the outbound interface. */ | |||||
cm = CMSG_FIRSTHDR(&hdr); | |||||
cm->cmsg_level = IPPROTO_IPV6; | cm->cmsg_level = IPPROTO_IPV6; | ||||
cm->cmsg_type = IPV6_PKTINFO; | cm->cmsg_type = IPV6_PKTINFO; | ||||
cm->cmsg_len = CMSG_LEN(sizeof(struct in6_pktinfo)); | cm->cmsg_len = CMSG_LEN(sizeof(struct in6_pktinfo)); | ||||
pi = (struct in6_pktinfo *)(void *)CMSG_DATA(cm); | pi = (struct in6_pktinfo *)(void *)CMSG_DATA(cm); | ||||
memset(&pi->ipi6_addr, 0, sizeof(pi->ipi6_addr)); /*XXX*/ | memset(&pi->ipi6_addr, 0, sizeof(pi->ipi6_addr)); /*XXX*/ | ||||
pi->ipi6_ifindex = ifindex; | pi->ipi6_ifindex = ifindex; | ||||
/* specify the hop limit of the packet for safety */ | /* Specify the hop limit of the packet for safety. */ | ||||
cm = CMSG_NXTHDR(&sndmhdr, cm); | hoplimit = 1; | ||||
cm = CMSG_NXTHDR(&hdr, cm); | |||||
cm->cmsg_level = IPPROTO_IPV6; | cm->cmsg_level = IPPROTO_IPV6; | ||||
cm->cmsg_type = IPV6_HOPLIMIT; | cm->cmsg_type = IPV6_HOPLIMIT; | ||||
cm->cmsg_len = CMSG_LEN(sizeof(int)); | cm->cmsg_len = CMSG_LEN(sizeof(int)); | ||||
memcpy(CMSG_DATA(cm), &hoplimit, sizeof(int)); | memcpy(CMSG_DATA(cm), &hoplimit, sizeof(int)); | ||||
warnmsg(LOG_DEBUG, __func__, "probe a router %s on %s", | warnmsg(LOG_DEBUG, __func__, "probe a router %s on %s", | ||||
inet_ntop(AF_INET6, addr, ntopbuf, INET6_ADDRSTRLEN), | inet_ntop(AF_INET6, addr, ntopbuf, INET6_ADDRSTRLEN), | ||||
if_indextoname(ifindex, ifnamebuf)); | if_indextoname(ifindex, ifnamebuf)); | ||||
if (sendmsg(probesock, &sndmhdr, 0)) | n = sendmsg(sock, &hdr, 0); | ||||
if (n != 0) { | |||||
error = errno; | |||||
warnmsg(LOG_ERR, __func__, "sendmsg on %s: %s", | warnmsg(LOG_ERR, __func__, "sendmsg on %s: %s", | ||||
if_indextoname(ifindex, ifnamebuf), strerror(errno)); | if_indextoname(ifindex, ifnamebuf), strerror(error)); | ||||
} | } | ||||
} | |||||
int | |||||
cap_probe_defrouters(cap_channel_t *cap, struct ifinfo *ifinfo) | |||||
{ | |||||
nvlist_t *nvl; | |||||
int error; | |||||
nvl = nvlist_create(0); | |||||
nvlist_add_string(nvl, "cmd", "probe_defrouters"); | |||||
nvlist_add_number(nvl, "ifindex", ifinfo->sdl->sdl_index); | |||||
nvlist_add_number(nvl, "linkid", ifinfo->linkid); | |||||
nvl = cap_xfer_nvlist(cap, nvl); | |||||
if (nvl == NULL) | |||||
return (errno); | |||||
error = 0; | |||||
if (nvlist_exists_number(nvl, "error")) | |||||
error = (int)nvlist_get_number(nvl, "error"); | |||||
oshogbo: Maybe just dnvlist ?
error = (int)dnvlist_get_number(nvl, "error", 0); | |||||
nvlist_destroy(nvl); | |||||
return (error); | |||||
} | |||||
static int | |||||
probe_command(const char *cmd, const nvlist_t *limits __unused, nvlist_t *nvlin, | |||||
nvlist_t *nvlout __unused) | |||||
{ | |||||
struct in6_defrouter *p, *ep; | |||||
char *buf; | |||||
int error, mib[4], sock; | |||||
size_t len; | |||||
uint32_t ifindex, linkid; | |||||
if (strcmp(cmd, "probe_defrouters") != 0) | |||||
return (EINVAL); | |||||
ifindex = (uint32_t)nvlist_get_number(nvlin, "ifindex"); | |||||
linkid = (uint32_t)nvlist_get_number(nvlin, "linkid"); | |||||
if (ifindex == 0) | |||||
return (EINVAL); | |||||
error = getsocket(&sock); | |||||
if (error != 0) | |||||
return (error); | |||||
mib[0] = CTL_NET; | |||||
mib[1] = PF_INET6; | |||||
mib[2] = IPPROTO_ICMPV6; | |||||
mib[3] = ICMPV6CTL_ND6_DRLIST; | |||||
if (sysctl(mib, nitems(mib), NULL, &len, NULL, 0) < 0) | |||||
return (errno); | |||||
if (len == 0) | |||||
return (0); | |||||
buf = malloc(len); | |||||
if (buf == NULL) | |||||
return (errno); | |||||
if (sysctl(mib, nitems(mib), buf, &len, NULL, 0) < 0) | |||||
return (errno); | |||||
ep = (struct in6_defrouter *)(void *)(buf + len); | |||||
for (p = (struct in6_defrouter *)(void *)buf; p < ep; p++) { | |||||
if (ifindex != p->if_index) | |||||
continue; | |||||
if (!IN6_IS_ADDR_LINKLOCAL(&p->rtaddr.sin6_addr)) | |||||
continue; | |||||
sendprobe(sock, &p->rtaddr.sin6_addr, ifindex, linkid); | |||||
} | |||||
free(buf); | |||||
return (0); | |||||
} | |||||
static int | |||||
probe_limit(const nvlist_t *oldlimits __unused, | |||||
const nvlist_t *newlimits __unused) | |||||
{ | |||||
return (0); | |||||
} | |||||
CREATE_SERVICE("rtsold.defrouter_probe", probe_limit, probe_command, 0); | |||||
Done Inline ActionsYou can set just NULL in case of probe_limit. oshogbo: You can set just NULL in case of probe_limit. |
Maybe just dnvlist ?
error = (int)dnvlist_get_number(nvl, "error", 0);