| Colin Cross | 6e3fffe | 2014-03-21 16:59:20 -0700 | [diff] [blame] | 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 <malloc.h> | 
 | 20 | #include <unistd.h> | 
 | 21 | #include <sys/socket.h> | 
 | 22 | #include <linux/netlink.h> | 
 | 23 | #include "netlink-types.h" | 
 | 24 |  | 
 | 25 | /* Allocate a new netlink message with the default maximum payload size. */ | 
 | 26 | struct nl_msg *nlmsg_alloc(void) | 
 | 27 | { | 
 | 28 | 	/* Whole page will store nl_msg + nlmsghdr + genlmsghdr + payload */ | 
 | 29 | 	const int page_sz = getpagesize(); | 
 | 30 | 	struct nl_msg *nm; | 
 | 31 | 	struct nlmsghdr *nlh; | 
 | 32 |  | 
 | 33 | 	/* Netlink message */ | 
 | 34 | 	nm = (struct nl_msg *) malloc(page_sz); | 
 | 35 | 	if (!nm) | 
 | 36 | 		goto fail; | 
 | 37 |  | 
 | 38 | 	/* Netlink message header pointer */ | 
 | 39 | 	nlh = (struct nlmsghdr *) ((char *) nm + sizeof(struct nl_msg)); | 
 | 40 |  | 
 | 41 | 	/* Initialize */ | 
 | 42 | 	memset(nm, 0, page_sz); | 
 | 43 | 	nm->nm_size = page_sz; | 
 | 44 |  | 
 | 45 | 	nm->nm_src.nl_family = AF_NETLINK; | 
 | 46 | 	nm->nm_src.nl_pid = getpid(); | 
 | 47 |  | 
 | 48 | 	nm->nm_dst.nl_family = AF_NETLINK; | 
 | 49 | 	nm->nm_dst.nl_pid = 0; /* Kernel */ | 
 | 50 |  | 
 | 51 | 	/* Initialize and add to netlink message */ | 
 | 52 | 	nlh->nlmsg_len = NLMSG_HDRLEN; | 
 | 53 | 	nm->nm_nlh = nlh; | 
 | 54 |  | 
 | 55 | 	/* Add to reference count and return nl_msg */ | 
 | 56 | 	nlmsg_get(nm); | 
 | 57 | 	return nm; | 
 | 58 | fail: | 
 | 59 | 	return NULL; | 
 | 60 | } | 
 | 61 |  | 
 | 62 | /* Return pointer to message payload. */ | 
 | 63 | void *nlmsg_data(const struct nlmsghdr *nlh) | 
 | 64 | { | 
 | 65 | 	return (char *) nlh + NLMSG_HDRLEN; | 
 | 66 | } | 
 | 67 |  | 
 | 68 | /* Add reference count to nl_msg */ | 
 | 69 | void nlmsg_get(struct nl_msg *nm) | 
 | 70 | { | 
 | 71 | 	nm->nm_refcnt++; | 
 | 72 | } | 
 | 73 |  | 
 | 74 | /* Release a reference from an netlink message. */ | 
 | 75 | void nlmsg_free(struct nl_msg *nm) | 
 | 76 | { | 
 | 77 | 	if (nm) { | 
 | 78 | 		nm->nm_refcnt--; | 
 | 79 | 		if (nm->nm_refcnt <= 0) | 
 | 80 | 			free(nm); | 
 | 81 | 	} | 
 | 82 |  | 
 | 83 | } | 
 | 84 |  | 
 | 85 | /* Return actual netlink message. */ | 
 | 86 | struct nlmsghdr *nlmsg_hdr(struct nl_msg *n) | 
 | 87 | { | 
 | 88 | 	return n->nm_nlh; | 
 | 89 | } | 
 | 90 |  | 
 | 91 | /* Return head of attributes data / payload section */ | 
 | 92 | struct nlattr *nlmsg_attrdata(const struct nlmsghdr *nlh, int hdrlen) | 
 | 93 | { | 
 | 94 | 	unsigned char *data = nlmsg_data(nlh); | 
 | 95 | 	return (struct nlattr *)(data + NLMSG_ALIGN(hdrlen)); | 
 | 96 | } | 
 | 97 |  | 
 | 98 | /* Returns pointer to end of netlink message */ | 
 | 99 | void *nlmsg_tail(const struct nlmsghdr *nlh) | 
 | 100 | { | 
 | 101 | 	return (void *)((char *)nlh + NLMSG_ALIGN(nlh->nlmsg_len)); | 
 | 102 | } | 
 | 103 |  | 
 | 104 | /* Next netlink message in message stream */ | 
 | 105 | struct nlmsghdr *nlmsg_next(struct nlmsghdr *nlh, int *remaining) | 
 | 106 | { | 
 | 107 | 	struct nlmsghdr *next_nlh = NULL; | 
 | 108 | 	int len = nlmsg_len(nlh); | 
 | 109 |  | 
 | 110 | 	len = NLMSG_ALIGN(len); | 
 | 111 | 	if (*remaining > 0 && | 
 | 112 | 	    len <= *remaining && | 
 | 113 | 	    len >= (int) sizeof(struct nlmsghdr)) { | 
 | 114 | 		next_nlh = (struct nlmsghdr *)((char *)nlh + len); | 
 | 115 | 		*remaining -= len; | 
 | 116 | 	} | 
 | 117 |  | 
 | 118 | 	return next_nlh; | 
 | 119 | } | 
 | 120 |  | 
 | 121 | int nlmsg_datalen(const struct nlmsghdr *nlh) | 
 | 122 | { | 
 | 123 | 	return nlh->nlmsg_len - NLMSG_HDRLEN; | 
 | 124 | } | 
 | 125 |  | 
 | 126 | /* Length of attributes data */ | 
 | 127 | int nlmsg_attrlen(const struct nlmsghdr *nlh, int hdrlen) | 
 | 128 | { | 
 | 129 | 	return nlmsg_datalen(nlh) - NLMSG_ALIGN(hdrlen); | 
 | 130 | } | 
 | 131 |  | 
 | 132 | /* Length of netlink message */ | 
 | 133 | int nlmsg_len(const struct nlmsghdr *nlh) | 
 | 134 | { | 
 | 135 | 	return nlh->nlmsg_len; | 
 | 136 | } | 
 | 137 |  | 
 | 138 | /* Check if the netlink message fits into the remaining bytes */ | 
 | 139 | int nlmsg_ok(const struct nlmsghdr *nlh, int rem) | 
 | 140 | { | 
 | 141 | 	return rem >= (int)sizeof(struct nlmsghdr) && | 
 | 142 | 		rem >= nlmsg_len(nlh) && | 
 | 143 | 		nlmsg_len(nlh) >= (int) sizeof(struct nlmsghdr) && | 
 | 144 | 		nlmsg_len(nlh) <= (rem); | 
 | 145 | } | 
 | 146 |  | 
 | 147 | int nlmsg_padlen(int payload) | 
 | 148 | { | 
 | 149 | 	return NLMSG_ALIGN(payload) - payload; | 
 | 150 | } |