Home | History | Annotate | Download | only in genl
      1 /*
      2  * Copyright (C) 2011 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *	http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 /* NOTICE: This is a clean room re-implementation of libnl */
     18 
     19 #include <errno.h>
     20 #include <unistd.h>
     21 #include <stdio.h>
     22 #include <sys/time.h>
     23 #include <sys/socket.h>
     24 #include <linux/netlink.h>
     25 #include <netlink/genl/ctrl.h>
     26 #include <netlink/genl/family.h>
     27 #include "netlink-types.h"
     28 
     29 /* Get head of attribute data. */
     30 struct nlattr *genlmsg_attrdata(const struct genlmsghdr *gnlh, int hdrlen)
     31 {
     32 	return (struct nlattr *) \
     33 		((char *) gnlh + GENL_HDRLEN + NLMSG_ALIGN(hdrlen));
     34 
     35 }
     36 
     37 /* Get length of attribute data. */
     38 int genlmsg_attrlen(const struct genlmsghdr *gnlh, int hdrlen)
     39 {
     40 	struct nlattr *nla;
     41 	struct nlmsghdr *nlh;
     42 
     43 	nla = genlmsg_attrdata(gnlh, hdrlen);
     44 	nlh = (struct nlmsghdr *) ((char *) gnlh - NLMSG_HDRLEN);
     45 	return (char *) nlmsg_tail(nlh) - (char *) nla;
     46 }
     47 
     48 /* Add generic netlink header to netlink message. */
     49 void *genlmsg_put(struct nl_msg *msg, uint32_t pid, uint32_t seq, int family,
     50 		int hdrlen, int flags, uint8_t cmd, uint8_t version)
     51 {
     52 	int new_size;
     53 	struct nlmsghdr *nlh;
     54 	struct timeval tv;
     55 	struct genlmsghdr *gmh;
     56 
     57 	/* Make sure nl_msg has enough space */
     58 	new_size = NLMSG_HDRLEN + GENL_HDRLEN + hdrlen;
     59 	if ((sizeof(struct nl_msg) + new_size) > msg->nm_size)
     60 		goto fail;
     61 
     62 	/* Fill in netlink header */
     63 	nlh = msg->nm_nlh;
     64 	nlh->nlmsg_len = new_size;
     65 	nlh->nlmsg_type = family;
     66 	nlh->nlmsg_pid = getpid();
     67 	nlh->nlmsg_flags = flags | NLM_F_REQUEST | NLM_F_ACK;
     68 
     69 	/* Get current time for sequence number */
     70 	if (gettimeofday(&tv, NULL))
     71 		nlh->nlmsg_seq = 1;
     72 	else
     73 		nlh->nlmsg_seq = (int) tv.tv_sec;
     74 
     75 	/* Setup genlmsghdr in new message */
     76 	gmh = (struct genlmsghdr *) ((char *)nlh + NLMSG_HDRLEN);
     77 	gmh->cmd = (__u8) cmd;
     78 	gmh->version = version;
     79 
     80 	return gmh;
     81 fail:
     82 	return NULL;
     83 
     84 }
     85 
     86 /* Socket has already been alloced to connect it to kernel? */
     87 int genl_connect(struct nl_sock *sk)
     88 {
     89 	return nl_connect(sk, NETLINK_GENERIC);
     90 
     91 }
     92 
     93 int genl_ctrl_alloc_cache(struct nl_sock *sock, struct nl_cache **result)
     94 {
     95 	int rc = -1;
     96 	int nl80211_genl_id = -1;
     97 	char sendbuf[sizeof(struct nlmsghdr)+sizeof(struct genlmsghdr)];
     98 	struct nlmsghdr nlmhdr;
     99 	struct genlmsghdr gmhhdr;
    100 	struct iovec sendmsg_iov;
    101 	struct msghdr msg;
    102 	int num_char;
    103 	const int RECV_BUF_SIZE = getpagesize();
    104 	char *recvbuf;
    105 	struct iovec recvmsg_iov;
    106 	int nl80211_flag = 0, nlm_f_multi = 0, nlmsg_done = 0;
    107 	struct nlmsghdr *nlh;
    108 
    109 	/* REQUEST GENERIC NETLINK FAMILY ID */
    110 	/* Message buffer */
    111 	nlmhdr.nlmsg_len = sizeof(sendbuf);
    112 	nlmhdr.nlmsg_type = NETLINK_GENERIC;
    113 	nlmhdr.nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK | NLM_F_DUMP;
    114 	nlmhdr.nlmsg_seq = sock->s_seq_next;
    115 	nlmhdr.nlmsg_pid = sock->s_local.nl_pid;
    116 
    117 	/* Generic netlink header */
    118 	memset(&gmhhdr, 0, sizeof(gmhhdr));
    119 	gmhhdr.cmd = CTRL_CMD_GETFAMILY;
    120 	gmhhdr.version = CTRL_ATTR_FAMILY_ID;
    121 
    122 	/* Combine netlink and generic netlink headers */
    123 	memcpy(&sendbuf[0], &nlmhdr, sizeof(nlmhdr));
    124 	memcpy(&sendbuf[0]+sizeof(nlmhdr), &gmhhdr, sizeof(gmhhdr));
    125 
    126 	/* Create IO vector with Netlink message */
    127 	sendmsg_iov.iov_base = &sendbuf;
    128 	sendmsg_iov.iov_len = sizeof(sendbuf);
    129 
    130 	/* Socket message */
    131 	msg.msg_name = (void *) &sock->s_peer;
    132 	msg.msg_namelen = sizeof(sock->s_peer);
    133 	msg.msg_iov = &sendmsg_iov;
    134 	msg.msg_iovlen = 1; /* Only sending one iov */
    135 	msg.msg_control = NULL;
    136 	msg.msg_controllen = 0;
    137 	msg.msg_flags = 0;
    138 
    139 	/* Send message and verify sent */
    140 	num_char = sendmsg(sock->s_fd, &msg, 0);
    141 	if (num_char == -1)
    142 		return -errno;
    143 
    144 	/* RECEIVE GENL CMD RESPONSE */
    145 
    146 	/* Create receive iov buffer */
    147 	recvbuf = (char *) malloc(RECV_BUF_SIZE);
    148 
    149 	/* Attach to iov */
    150 	recvmsg_iov.iov_base = recvbuf;
    151 	recvmsg_iov.iov_len = RECV_BUF_SIZE;
    152 
    153 	msg.msg_iov = &recvmsg_iov;
    154 	msg.msg_iovlen = 1;
    155 
    156 	/***************************************************************/
    157 	/* Receive message. If multipart message, keep receiving until */
    158 	/* message type is NLMSG_DONE				       */
    159 	/***************************************************************/
    160 
    161 	do {
    162 
    163 		int recvmsg_len, nlmsg_rem;
    164 
    165 		/* Receive message */
    166 		memset(recvbuf, 0, RECV_BUF_SIZE);
    167 		recvmsg_len = recvmsg(sock->s_fd, &msg, 0);
    168 
    169 		/* Make sure receive successful */
    170 		if (recvmsg_len < 0) {
    171 			rc = -errno;
    172 			goto error_recvbuf;
    173 		}
    174 
    175 		/* Parse nlmsghdr */
    176 		nlmsg_for_each_msg(nlh, (struct nlmsghdr *) recvbuf, \
    177 				recvmsg_len, nlmsg_rem) {
    178 			struct nlattr *nla;
    179 			int nla_rem;
    180 
    181 			/* Check type */
    182 			switch (nlh->nlmsg_type) {
    183 			case NLMSG_DONE:
    184 				goto return_genl_id;
    185 				break;
    186 			case NLMSG_ERROR:
    187 
    188 				/* Should check nlmsgerr struct received */
    189 				fprintf(stderr, "Receive message error\n");
    190 				goto error_recvbuf;
    191 			case NLMSG_OVERRUN:
    192 				fprintf(stderr, "Receive data partly lost\n");
    193 				goto error_recvbuf;
    194 			case NLMSG_MIN_TYPE:
    195 			case NLMSG_NOOP:
    196 				break;
    197 			default:
    198 				break;
    199 			}
    200 
    201 
    202 
    203 			/* Check flags */
    204 			if (nlh->nlmsg_flags & NLM_F_MULTI)
    205 				nlm_f_multi = 1;
    206 			else
    207 				nlm_f_multi = 0;
    208 
    209 			if (nlh->nlmsg_type & NLMSG_DONE)
    210 				nlmsg_done = 1;
    211 			else
    212 				nlmsg_done = 0;
    213 
    214 			/* Iteratve over attributes */
    215 			nla_for_each_attr(nla,
    216 					nlmsg_attrdata(nlh, GENL_HDRLEN),
    217 					nlmsg_attrlen(nlh, GENL_HDRLEN),
    218 					nla_rem){
    219 
    220 				/* If this family is nl80211 */
    221 				if (nla->nla_type == CTRL_ATTR_FAMILY_NAME &&
    222 					!strcmp((char *)nla_data(nla),
    223 						"nl80211"))
    224 					nl80211_flag = 1;
    225 
    226 				/* Save the family id */
    227 				else if (nl80211_flag &&
    228 					nla->nla_type == CTRL_ATTR_FAMILY_ID) {
    229 					nl80211_genl_id =
    230 						*((int *)nla_data(nla));
    231 					nl80211_flag = 0;
    232 				}
    233 
    234 			}
    235 
    236 		}
    237 
    238 	} while (nlm_f_multi && !nlmsg_done);
    239 
    240 return_genl_id:
    241 	/* Return family id as cache pointer */
    242 	*result = (struct nl_cache *) nl80211_genl_id;
    243 	rc = 0;
    244 error_recvbuf:
    245 	free(recvbuf);
    246 error:
    247 	return rc;
    248 }
    249 
    250 /* Checks the netlink cache to find family reference by name string */
    251 /* NOTE: Caller needs to call genl_family_put() when done with *
    252  * returned object */
    253 struct genl_family *genl_ctrl_search_by_name(struct nl_cache *cache, \
    254 					const char *name)
    255 {
    256 	struct genl_family *gf = (struct genl_family *) \
    257 		malloc(sizeof(struct genl_family));
    258 	if (!gf)
    259 		goto fail;
    260 	memset(gf, 0, sizeof(*gf));
    261 
    262 	/* Add ref */
    263 	gf->ce_refcnt++;
    264 
    265 	/* Overriding cache pointer as family id for now */
    266 	gf->gf_id = (uint16_t) ((uint32_t) cache);
    267 	strncpy(gf->gf_name, name, GENL_NAMSIZ);
    268 
    269 	return gf;
    270 fail:
    271 	return NULL;
    272 
    273 }
    274 
    275 int genl_ctrl_resolve(struct nl_sock *sk, const char *name)
    276 {
    277 	struct nl_cache *cache = NULL;
    278 	struct genl_family *gf = NULL;
    279 	int id = -1;
    280 
    281 	/* Hack to support wpa_supplicant */
    282 	if (strcmp(name, "nlctrl") == 0)
    283 		return NETLINK_GENERIC;
    284 
    285 	if (strcmp(name, "nl80211") != 0) {
    286 		fprintf(stderr, "%s is not supported\n", name);
    287 		return id;
    288 	}
    289 
    290 	if (!genl_ctrl_alloc_cache(sk, &cache)) {
    291 		gf = genl_ctrl_search_by_name(cache, name);
    292 		if (gf)
    293 			id = genl_family_get_id(gf);
    294 	}
    295 
    296 	if (gf)
    297 		genl_family_put(gf);
    298 	if (cache)
    299 		nl_cache_free(cache);
    300 
    301 	return id;
    302 }
    303