Home | History | Annotate | Download | only in tipc
      1 /*
      2  * msg.c	Messaging (netlink) helper functions.
      3  *
      4  *		This program is free software; you can redistribute it and/or
      5  *		modify it under the terms of the GNU General Public License
      6  *		as published by the Free Software Foundation; either version
      7  *		2 of the License, or (at your option) any later version.
      8  *
      9  * Authors:	Richard Alpe <richard.alpe (at) ericsson.com>
     10  */
     11 
     12 #include <stdio.h>
     13 #include <time.h>
     14 #include <errno.h>
     15 
     16 #include <linux/tipc_netlink.h>
     17 #include <linux/tipc.h>
     18 #include <linux/genetlink.h>
     19 #include <libmnl/libmnl.h>
     20 
     21 #include "msg.h"
     22 
     23 int parse_attrs(const struct nlattr *attr, void *data)
     24 {
     25 	const struct nlattr **tb = data;
     26 	int type = mnl_attr_get_type(attr);
     27 
     28 	tb[type] = attr;
     29 
     30 	return MNL_CB_OK;
     31 }
     32 
     33 static int family_id_cb(const struct nlmsghdr *nlh, void *data)
     34 {
     35 	struct nlattr *tb[CTRL_ATTR_MAX + 1] = {};
     36 	struct genlmsghdr *genl = mnl_nlmsg_get_payload(nlh);
     37 	int *id = data;
     38 
     39 	mnl_attr_parse(nlh, sizeof(*genl), parse_attrs, tb);
     40 	if (!tb[CTRL_ATTR_FAMILY_ID])
     41 		return MNL_CB_ERROR;
     42 
     43 	*id = mnl_attr_get_u16(tb[CTRL_ATTR_FAMILY_ID]);
     44 
     45 	return MNL_CB_OK;
     46 }
     47 
     48 static struct mnl_socket *msg_send(struct nlmsghdr *nlh)
     49 {
     50 	int ret;
     51 	struct mnl_socket *nl;
     52 
     53 	nl = mnl_socket_open(NETLINK_GENERIC);
     54 	if (nl == NULL) {
     55 		perror("mnl_socket_open");
     56 		return NULL;
     57 	}
     58 
     59 	ret = mnl_socket_bind(nl, 0, MNL_SOCKET_AUTOPID);
     60 	if (ret < 0) {
     61 		perror("mnl_socket_bind");
     62 		return NULL;
     63 	}
     64 
     65 	ret = mnl_socket_sendto(nl, nlh, nlh->nlmsg_len);
     66 	if (ret < 0) {
     67 		perror("mnl_socket_send");
     68 		return NULL;
     69 	}
     70 
     71 	return nl;
     72 }
     73 
     74 static int msg_recv(struct mnl_socket *nl, mnl_cb_t callback, void *data, int seq)
     75 {
     76 	int ret;
     77 	unsigned int portid;
     78 	char buf[MNL_SOCKET_BUFFER_SIZE];
     79 
     80 	portid = mnl_socket_get_portid(nl);
     81 
     82 	ret = mnl_socket_recvfrom(nl, buf, sizeof(buf));
     83 	while (ret > 0) {
     84 		ret = mnl_cb_run(buf, ret, seq, portid, callback, data);
     85 		if (ret <= 0)
     86 			break;
     87 		ret = mnl_socket_recvfrom(nl, buf, sizeof(buf));
     88 	}
     89 	if (ret == -1)
     90 		perror("error");
     91 
     92 	mnl_socket_close(nl);
     93 
     94 	return ret;
     95 }
     96 
     97 static int msg_query(struct nlmsghdr *nlh, mnl_cb_t callback, void *data)
     98 {
     99 	unsigned int seq;
    100 	struct mnl_socket *nl;
    101 
    102 	seq = time(NULL);
    103 	nlh->nlmsg_seq = seq;
    104 
    105 	nl = msg_send(nlh);
    106 	if (!nl)
    107 		return -ENOTSUP;
    108 
    109 	return msg_recv(nl, callback, data, seq);
    110 }
    111 
    112 static int get_family(void)
    113 {
    114 	int err;
    115 	int nl_family;
    116 	struct nlmsghdr *nlh;
    117 	struct genlmsghdr *genl;
    118 	char buf[MNL_SOCKET_BUFFER_SIZE];
    119 
    120 	nlh = mnl_nlmsg_put_header(buf);
    121 	nlh->nlmsg_type	= GENL_ID_CTRL;
    122 	nlh->nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK;
    123 
    124 	genl = mnl_nlmsg_put_extra_header(nlh, sizeof(struct genlmsghdr));
    125 	genl->cmd = CTRL_CMD_GETFAMILY;
    126 	genl->version = 1;
    127 
    128 	mnl_attr_put_u32(nlh, CTRL_ATTR_FAMILY_ID, GENL_ID_CTRL);
    129 	mnl_attr_put_strz(nlh, CTRL_ATTR_FAMILY_NAME, TIPC_GENL_V2_NAME);
    130 
    131 	if ((err = msg_query(nlh, family_id_cb, &nl_family)))
    132 		return err;
    133 
    134 	return nl_family;
    135 }
    136 
    137 int msg_doit(struct nlmsghdr *nlh, mnl_cb_t callback, void *data)
    138 {
    139 	nlh->nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK;
    140 	return msg_query(nlh, callback, data);
    141 }
    142 
    143 int msg_dumpit(struct nlmsghdr *nlh, mnl_cb_t callback, void *data)
    144 {
    145 	nlh->nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP;
    146 	return msg_query(nlh, callback, data);
    147 }
    148 
    149 struct nlmsghdr *msg_init(char *buf, int cmd)
    150 {
    151 	int family;
    152 	struct nlmsghdr *nlh;
    153 	struct genlmsghdr *genl;
    154 
    155 	family = get_family();
    156 	if (family <= 0) {
    157 		fprintf(stderr,
    158 			"Unable to get TIPC nl family id (module loaded?)\n");
    159 		return NULL;
    160 	}
    161 
    162 	nlh = mnl_nlmsg_put_header(buf);
    163 	nlh->nlmsg_type	= family;
    164 
    165 	genl = mnl_nlmsg_put_extra_header(nlh, sizeof(struct genlmsghdr));
    166 	genl->cmd = cmd;
    167 	genl->version = 1;
    168 
    169 	return nlh;
    170 }
    171