#include <sys/socket.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <sys/poll.h>
#include <sys/param.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <linux/socket.h>
#include <linux/tipc.h>
#include "tipcc.h"
#ifndef TIPC_SERVICE_ADDR
#define tipc_socket_addr tipc_portid
#define tipc_service_addr tipc_name
#define tipc_service_range tipc_name_seq
#define TIPC_SERVICE_RANGE 1
#define TIPC_SERVICE_ADDR 2
#define TIPC_SOCKET_ADDR 3
#define TIPC_TOP_SRV 1
#endif
static uint32_t own_node = 0;
static inline __u8 node2scope(uint32_t node)
{
if (tipc_own_node() == node)
return TIPC_NODE_SCOPE;
else
return TIPC_CLUSTER_SCOPE;
}
uint32_t tipc_own_node(void)
{
struct tipc_addr socket;
int sd;
if (own_node)
return own_node;
sd = tipc_socket(SOCK_RDM);
if (tipc_sockaddr(sd, &socket) == 0) {
own_node = socket.node;
}
close(sd);
return own_node;
}
int tipc_socket(int sk_type)
{
return socket(AF_TIPC, sk_type, 0);
}
int tipc_sock_non_block(int sd)
{
int flags;
flags = fcntl(sd, F_GETFL, 0);
if (flags < 0)
return -1;
flags = O_NONBLOCK;
if (fcntl(sd, F_SETFL, flags) < 0)
return -1;
return sd;
}
int tipc_sock_rejectable(int sd)
{
int val = 0;
return setsockopt(sd, SOL_TIPC, TIPC_DEST_DROPPABLE,
&val, sizeof(val));
}
int tipc_sock_importance(int sd, uint32_t priority)
{
return setsockopt(sd, SOL_TIPC, TIPC_IMPORTANCE,
&priority, sizeof(priority));
}
int tipc_close(int sd)
{
return close(sd);
}
int tipc_sockaddr(int sd, struct tipc_addr *addr)
{
struct sockaddr_tipc saddr;
socklen_t sz = sizeof(saddr);
if (!addr)
return -1;
if (getsockname(sd, (struct sockaddr *)&saddr, &sz) < 0)
return -1;
addr->type = 0;
addr->instance = saddr.addr.id.ref;
addr->node = saddr.addr.id.node;
return 0;
}
int tipc_bind(int sd, uint32_t type, uint32_t lower, uint32_t upper,
uint32_t scope)
{
struct sockaddr_tipc addr = {
.family = AF_TIPC,
.addrtype = TIPC_SERVICE_RANGE,
.scope = scope,
.addr.nameseq.type = type,
.addr.nameseq.lower = lower,
.addr.nameseq.upper = upper
};
return bind(sd, (struct sockaddr *)&addr, sizeof(addr));
}
int tipc_unbind(int sd, uint32_t type, uint32_t lower, uint32_t upper)
{
struct sockaddr_tipc addr = {
.family = AF_TIPC,
.addrtype = TIPC_SERVICE_RANGE,
.scope = -1,
.addr.nameseq.type = type,
.addr.nameseq.lower = lower,
.addr.nameseq.upper = upper
};
return bind(sd, (struct sockaddr *)&addr, sizeof(addr));
}
int tipc_connect(int sd, const struct tipc_addr *dst)
{
struct sockaddr_tipc addr;
if (!dst)
return -1;
addr.family = AF_TIPC;
addr.addrtype = TIPC_SERVICE_ADDR;
addr.addr.name.name.type = dst->type;
addr.addr.name.name.instance = dst->instance;
addr.addr.name.domain = dst->node;
return connect(sd, (struct sockaddr*)&addr, sizeof(addr));
}
int tipc_listen(int sd, int backlog)
{
return listen(sd, backlog);
}
int tipc_accept(int sd, struct tipc_addr *src)
{
struct sockaddr_tipc addr;
socklen_t addrlen = sizeof(addr);
int rc;
rc = accept(sd, (struct sockaddr *) &addr, &addrlen);
if (src) {
src->type = 0;
src->instance = addr.addr.id.ref;
src->node = addr.addr.id.node;
}
return rc;
}
int tipc_join(int sd, struct tipc_addr *memberid, bool events, bool loopback)
{
#ifdef TIPC_GROUP_JOIN
uint32_t node = memberid->node;
struct tipc_group_req mreq = {
.type = memberid->type,
.instance = memberid->instance,
.scope = memberid->scope,
};
if (node && node != tipc_own_node())
return -1;
mreq.flags = loopback ? TIPC_GROUP_LOOPBACK : 0;
mreq.flags |= events ? TIPC_GROUP_MEMBER_EVTS : 0;
return setsockopt(sd, SOL_TIPC, TIPC_GROUP_JOIN, &mreq, sizeof(mreq));
#else
#warning "tipc_join() not supported by this kernel version"
printf("tipc_join() not supported by kernel version this was built for\n");
return -1;
#endif
}
int tipc_leave(int sd)
{
#ifdef TIPC_GROUP_LEAVE
return setsockopt(sd, SOL_TIPC, TIPC_GROUP_LEAVE, NULL, 0);
#else
#warning "tipc_leave() not supported by this kernel version"
printf("tipc_leave() not supported by kernel version this was built for\n");
return -1;
#endif
}
int tipc_send(int sd, const void *msg, size_t msg_len)
{
return send(sd, msg, msg_len, 0);
}
int tipc_sendmsg(int sd, const struct msghdr *msg)
{
return sendmsg(sd, msg, 0);
}
int tipc_sendto(int sd, const void *msg, size_t msg_len,
const struct tipc_addr *dst)
{
struct sockaddr_tipc addr;
if(!dst)
return -1;
addr.family = AF_TIPC;
if (dst->type) {
addr.addrtype = TIPC_SERVICE_ADDR;
addr.addr.name.name.type = dst->type;
addr.addr.name.name.instance = dst->instance;
addr.addr.name.domain = dst->node;
} else {
addr.addrtype = TIPC_ADDR_ID;
addr.addr.id.ref = dst->instance;
addr.addr.id.node = dst->node;
}
return sendto(sd, msg, msg_len, 0,
(struct sockaddr*)&addr, sizeof(addr));
}
int tipc_mcast(int sd, const void *msg, size_t msg_len,
const struct tipc_addr *dst)
{
struct sockaddr_tipc addr = {
.family = AF_TIPC,
.addrtype = TIPC_ADDR_MCAST,
};
if(!dst) {
return -1;
}
addr.scope = dst->scope;
addr.addr.nameseq.type = dst->type;
addr.addr.nameseq.lower = dst->instance;
addr.addr.nameseq.upper = dst->node;
return sendto(sd, msg, msg_len, 0,
(struct sockaddr*)&addr, sizeof(addr));
}
int tipc_recv(int sd, void *buf, size_t buf_len, bool waitall)
{
int flags = waitall ? MSG_WAITALL : 0;
return recv(sd, buf, buf_len, flags);
}
int tipc_recvfrom(int sd, void *buf, size_t len, struct tipc_addr *sockid,
struct tipc_addr *memberid, int *err)
{
struct sockaddr_tipc addr[2];
struct iovec iov = {buf, len};
struct msghdr msg = {0, };
struct cmsghdr *anc;
char anc_space[CMSG_SPACE(8) + CMSG_SPACE(1024) + CMSG_SPACE(16)];
int rc, _err = 0;
msg.msg_name = &addr;
msg.msg_namelen = sizeof(addr);
msg.msg_iov = &iov;
msg.msg_iovlen = 1;
msg.msg_control = (struct cmsghdr *)anc_space;
msg.msg_controllen = sizeof(anc_space);
rc = recvmsg(sd ,&msg ,0);
if (rc < 0)
return rc;
if (memberid) {
if (msg.msg_namelen == sizeof(addr)) {
memberid->type = addr[1].addr.name.name.type;
memberid->instance = addr[1].addr.name.name.instance;
} else {
memberid->type = 0;
memberid->instance = 0;
}
memberid->node = 0;
}
if (sockid) {
sockid->type = 0;
sockid->instance = addr[0].addr.id.ref;
sockid->node = addr[0].addr.id.node;
}
if (err)
*err = 0;
if (msg.msg_flags & MSG_OOB) {
if (rc)
return -1;
if (!err)
return 0;
if (msg.msg_flags & MSG_EOR)
*err = -1;
else
*err = 0;
return 0;
}
anc = CMSG_FIRSTHDR(&msg);
if (!anc || anc->cmsg_type != TIPC_ERRINFO)
return rc;
_err = *(int*)(CMSG_DATA(anc));
rc = MIN(*(int*)(CMSG_DATA(anc) + 4), len);
anc = CMSG_NXTHDR(&msg, anc);
memcpy(buf, (char*)CMSG_DATA(anc), rc);
anc = CMSG_NXTHDR(&msg, anc);
tipc_sockaddr(sd, sockid);
if (err)
*err = _err;
else if (_err)
rc = 0;
return rc;
}
int tipc_topsrv_conn(uint32_t node)
{
int sd;
struct tipc_addr srv = {TIPC_TOP_SRV, TIPC_TOP_SRV, node, TIPC_CLUSTER_SCOPE};
sd = tipc_socket(SOCK_SEQPACKET);
if (sd <= 0)
return sd;
if (tipc_connect(sd, &srv) < 0)
tipc_close(sd);
return sd;
}
int tipc_srv_subscr(int sd, uint32_t type, uint32_t lower,
uint32_t upper, bool all, int expire)
{
struct tipc_subscr subscr;
subscr.seq.type = type;
subscr.seq.lower = lower;
subscr.seq.upper = upper;
subscr.timeout = expire < 0 ? TIPC_WAIT_FOREVER : expire;
subscr.filter = all ? TIPC_SUB_PORTS : TIPC_SUB_SERVICE;
if (send(sd, &subscr, sizeof(subscr), 0) != sizeof(subscr))
return -1;
return 0;
}
int tipc_srv_evt(int sd, struct tipc_addr *srv, struct tipc_addr *id,
bool *available, bool *expired)
{
struct tipc_event evt;
if (recv(sd, &evt, sizeof(evt), 0) != sizeof(evt))
return -1;
if (evt.event == TIPC_SUBSCR_TIMEOUT) {
if (expired)
*expired = true;
return 0;
}
if (srv) {
srv->type = evt.s.seq.type;
srv->instance = evt.found_lower;
srv->node = evt.port.node;
}
if (id) {
id->type = 0;
id->instance = evt.port.ref;
id->node = evt.port.node;
}
if (available)
*available = (evt.event == TIPC_PUBLISHED);
if (expired)
*expired = false;
return 0;
}
bool tipc_srv_wait(const struct tipc_addr *srv, int wait)
{
uint32_t node = srv->node;
struct tipc_addr addr;
bool expired = false;
bool up = false;
int sd;
sd = tipc_topsrv_conn(0);
if (sd < 0)
return false;
if (tipc_srv_subscr(sd, srv->type, srv->instance,
srv->instance, false, wait))
goto exit;
while (!tipc_srv_evt(sd, &addr, 0, &up, &expired)) {
if (expired)
break;
if (node && node != addr.node)
continue;
if (up)
break;
}
exit:
close(sd);
return up && !expired;
}
int tipc_neigh_subscr(uint32_t node)
{
int sd;
sd = tipc_topsrv_conn(node);
if (sd <= 0)
return -1;
if (tipc_srv_subscr(sd, TIPC_CFG_SRV, 0, ~0, true, TIPC_WAIT_FOREVER))
return -1;
return sd;
}
int tipc_neigh_evt(int sd, uint32_t *neigh_node, bool *available)
{
struct tipc_addr srv;
int rc;
rc = tipc_srv_evt(sd, &srv, 0, available, 0);
if (neigh_node)
*neigh_node = srv.instance;
return rc;
}
int tipc_link_subscr(uint32_t node)
{
int sd;
sd = tipc_topsrv_conn(node);
if (sd <= 0)
return -1;
if (tipc_srv_subscr(sd, TIPC_LINK_STATE, 0, ~0,
true, TIPC_WAIT_FOREVER))
return -1;
return sd;
}
int tipc_link_evt(int sd, uint32_t *neigh_node, bool *available,
int *local_bearerid, int *remote_bearerid)
{
struct tipc_event evt;
if (recv(sd, &evt, sizeof(evt), 0) != sizeof(evt))
return -1;
if (local_bearerid)
*local_bearerid = evt.port.ref & 0xffff;
if (remote_bearerid)
*remote_bearerid = (evt.port.ref >> 16) & 0xffff;
if (neigh_node)
*neigh_node = evt.found_lower;
if (available)
*available = (evt.event == TIPC_PUBLISHED);
return 0;
}
char* tipc_linkname(char *buf, size_t len, uint32_t peer, int bearerid)
{
struct tipc_sioc_ln_req req = {peer, bearerid, {'\0'}};
int sd, rc;
buf[0] = 0;
sd = tipc_socket(SOCK_RDM);
if (sd <= 0)
return buf;
rc = ioctl(sd, SIOCGETLINKNAME, &req);
if (rc < 0)
return buf;
strncpy(buf, req.linkname, len);
buf[len] = 0;
return buf;
}
char* tipc_ntoa(const struct tipc_addr *addr, char *buf, size_t len)
{
if (addr->type)
snprintf(buf, len, "%u:%u@%x",
addr->type, addr->instance, addr->node);
else
snprintf(buf, len, "0:%010u@%x",
addr->instance, addr->node);
buf[len] = 0;
return buf;
}
char* tipc_rtoa(uint32_t type, uint32_t lower, uint32_t upper,
uint32_t node, char *buf, size_t len)
{
snprintf(buf, len, "%u:%u:%u@%x", type, lower, upper, node);
buf[len] = 0;
return buf;
}