1 /* iftable - table of network interfaces 2 * 3 * (C) 2004 by Astaro AG, written by Harald Welte <hwelte@astaro.com> 4 * (C) 2008 by Pablo Neira Ayuso <pablo@netfilter.org> 5 * 6 * This software is Free Software and licensed under GNU GPLv2. 7 */ 8 9 /* IFINDEX handling */ 10 11 #include <unistd.h> 12 #include <stdlib.h> 13 #include <stdio.h> 14 #include <string.h> 15 #include <sys/types.h> 16 #include <netinet/in.h> 17 #include <arpa/inet.h> 18 #include <errno.h> 19 #include <assert.h> 20 21 #include <linux/netdevice.h> 22 23 #include <libnfnetlink/libnfnetlink.h> 24 #include "rtnl.h" 25 #include "linux_list.h" 26 27 struct ifindex_node { 28 struct list_head head; 29 30 u_int32_t index; 31 u_int32_t type; 32 u_int32_t alen; 33 u_int32_t flags; 34 char addr[8]; 35 char name[16]; 36 }; 37 38 struct nlif_handle { 39 struct list_head ifindex_hash[16]; 40 struct rtnl_handle *rtnl_handle; 41 struct rtnl_handler ifadd_handler; 42 struct rtnl_handler ifdel_handler; 43 }; 44 45 /* iftable_add - Add/Update an entry to/in the interface table 46 * @n: netlink message header of a RTM_NEWLINK message 47 * @arg: not used 48 * 49 * This function adds/updates an entry in the intrface table. 50 * Returns -1 on error, 1 on success. 51 */ 52 static int iftable_add(struct nlmsghdr *n, void *arg) 53 { 54 unsigned int hash, found = 0; 55 struct ifinfomsg *ifi_msg = NLMSG_DATA(n); 56 struct ifindex_node *this; 57 struct rtattr *cb[IFLA_MAX+1]; 58 struct nlif_handle *h = arg; 59 60 if (n->nlmsg_type != RTM_NEWLINK) 61 return -1; 62 63 if (n->nlmsg_len < NLMSG_LENGTH(sizeof(ifi_msg))) 64 return -1; 65 66 rtnl_parse_rtattr(cb, IFLA_MAX, IFLA_RTA(ifi_msg), IFLA_PAYLOAD(n)); 67 68 if (!cb[IFLA_IFNAME]) 69 return -1; 70 71 hash = ifi_msg->ifi_index & 0xF; 72 list_for_each_entry(this, &h->ifindex_hash[hash], head) { 73 if (this->index == ifi_msg->ifi_index) { 74 found = 1; 75 break; 76 } 77 } 78 79 if (!found) { 80 this = malloc(sizeof(*this)); 81 if (!this) 82 return -1; 83 84 this->index = ifi_msg->ifi_index; 85 } 86 87 this->type = ifi_msg->ifi_type; 88 this->flags = ifi_msg->ifi_flags; 89 if (cb[IFLA_ADDRESS]) { 90 unsigned int alen; 91 this->alen = alen = RTA_PAYLOAD(cb[IFLA_ADDRESS]); 92 if (alen > sizeof(this->addr)) 93 alen = sizeof(this->addr); 94 memcpy(this->addr, RTA_DATA(cb[IFLA_ADDRESS]), alen); 95 } else { 96 this->alen = 0; 97 memset(this->addr, 0, sizeof(this->addr)); 98 } 99 strcpy(this->name, RTA_DATA(cb[IFLA_IFNAME])); 100 101 if (!found) 102 list_add(&this->head, &h->ifindex_hash[hash]); 103 104 return 1; 105 } 106 107 /* iftable_del - Delete an entry from the interface table 108 * @n: netlink message header of a RTM_DELLINK nlmsg 109 * @arg: not used 110 * 111 * Delete an entry from the interface table. 112 * Returns -1 on error, 0 if no matching entry was found or 1 on success. 113 */ 114 static int iftable_del(struct nlmsghdr *n, void *arg) 115 { 116 struct ifinfomsg *ifi_msg = NLMSG_DATA(n); 117 struct rtattr *cb[IFLA_MAX+1]; 118 struct nlif_handle *h = arg; 119 struct ifindex_node *this, *tmp; 120 unsigned int hash; 121 122 if (n->nlmsg_type != RTM_DELLINK) 123 return -1; 124 125 if (n->nlmsg_len < NLMSG_LENGTH(sizeof(ifi_msg))) 126 return -1; 127 128 rtnl_parse_rtattr(cb, IFLA_MAX, IFLA_RTA(ifi_msg), IFLA_PAYLOAD(n)); 129 130 hash = ifi_msg->ifi_index & 0xF; 131 list_for_each_entry_safe(this, tmp, &h->ifindex_hash[hash], head) { 132 if (this->index == ifi_msg->ifi_index) { 133 list_del(&this->head); 134 free(this); 135 return 1; 136 } 137 } 138 139 return 0; 140 } 141 142 /** Get the name for an ifindex 143 * 144 * \param nlif_handle A pointer to a ::nlif_handle created 145 * \param index ifindex to be resolved 146 * \param name interface name, pass a buffer of IFNAMSIZ size 147 * \return -1 on error, 1 on success 148 */ 149 int nlif_index2name(struct nlif_handle *h, 150 unsigned int index, 151 char *name) 152 { 153 unsigned int hash; 154 struct ifindex_node *this; 155 156 assert(h != NULL); 157 assert(name != NULL); 158 159 if (index == 0) { 160 strcpy(name, "*"); 161 return 1; 162 } 163 164 hash = index & 0xF; 165 list_for_each_entry(this, &h->ifindex_hash[hash], head) { 166 if (this->index == index) { 167 strcpy(name, this->name); 168 return 1; 169 } 170 } 171 172 errno = ENOENT; 173 return -1; 174 } 175 176 /** Get the flags for an ifindex 177 * 178 * \param nlif_handle A pointer to a ::nlif_handle created 179 * \param index ifindex to be resolved 180 * \param flags pointer to variable used to store the interface flags 181 * \return -1 on error, 1 on success 182 */ 183 int nlif_get_ifflags(const struct nlif_handle *h, 184 unsigned int index, 185 unsigned int *flags) 186 { 187 unsigned int hash; 188 struct ifindex_node *this; 189 190 assert(h != NULL); 191 assert(flags != NULL); 192 193 if (index == 0) { 194 errno = ENOENT; 195 return -1; 196 } 197 198 hash = index & 0xF; 199 list_for_each_entry(this, &h->ifindex_hash[hash], head) { 200 if (this->index == index) { 201 *flags = this->flags; 202 return 1; 203 } 204 } 205 errno = ENOENT; 206 return -1; 207 } 208 209 /** Initialize interface table 210 * 211 * Initialize rtnl interface and interface table 212 * Call this before any nlif_* function 213 * 214 * \return file descriptor to netlink socket 215 */ 216 struct nlif_handle *nlif_open(void) 217 { 218 int i; 219 struct nlif_handle *h; 220 221 h = calloc(1, sizeof(struct nlif_handle)); 222 if (h == NULL) 223 goto err; 224 225 for (i=0; i<16; i++) 226 INIT_LIST_HEAD(&h->ifindex_hash[i]); 227 228 h->ifadd_handler.nlmsg_type = RTM_NEWLINK; 229 h->ifadd_handler.handlefn = iftable_add; 230 h->ifadd_handler.arg = h; 231 h->ifdel_handler.nlmsg_type = RTM_DELLINK; 232 h->ifdel_handler.handlefn = iftable_del; 233 h->ifdel_handler.arg = h; 234 235 h->rtnl_handle = rtnl_open(); 236 if (h->rtnl_handle == NULL) 237 goto err; 238 239 if (rtnl_handler_register(h->rtnl_handle, &h->ifadd_handler) < 0) 240 goto err_close; 241 242 if (rtnl_handler_register(h->rtnl_handle, &h->ifdel_handler) < 0) 243 goto err_unregister; 244 245 return h; 246 247 err_unregister: 248 rtnl_handler_unregister(h->rtnl_handle, &h->ifadd_handler); 249 err_close: 250 rtnl_close(h->rtnl_handle); 251 free(h); 252 err: 253 return NULL; 254 } 255 256 /** Destructor of interface table 257 * 258 * \param nlif_handle A pointer to a ::nlif_handle created 259 * via nlif_open() 260 */ 261 void nlif_close(struct nlif_handle *h) 262 { 263 int i; 264 struct ifindex_node *this, *tmp; 265 266 assert(h != NULL); 267 268 rtnl_handler_unregister(h->rtnl_handle, &h->ifadd_handler); 269 rtnl_handler_unregister(h->rtnl_handle, &h->ifdel_handler); 270 rtnl_close(h->rtnl_handle); 271 272 for (i=0; i<16; i++) { 273 list_for_each_entry_safe(this, tmp, &h->ifindex_hash[i], head) { 274 list_del(&this->head); 275 free(this); 276 } 277 } 278 279 free(h); 280 h = NULL; /* bugtrap */ 281 } 282 283 /** Receive message from netlink and update interface table 284 * 285 * \param nlif_handle A pointer to a ::nlif_handle created 286 * \return 0 if OK 287 */ 288 int nlif_catch(struct nlif_handle *h) 289 { 290 assert(h != NULL); 291 292 if (h->rtnl_handle) 293 return rtnl_receive(h->rtnl_handle); 294 295 return -1; 296 } 297 298 static int nlif_catch_multi(struct nlif_handle *h) 299 { 300 assert(h != NULL); 301 302 if (h->rtnl_handle) 303 return rtnl_receive_multi(h->rtnl_handle); 304 305 return -1; 306 } 307 308 /** 309 * nlif_query - request a dump of interfaces available in the system 310 * @h: pointer to a valid nlif_handler 311 */ 312 int nlif_query(struct nlif_handle *h) 313 { 314 assert(h != NULL); 315 316 if (rtnl_dump_type(h->rtnl_handle, RTM_GETLINK) < 0) 317 return -1; 318 319 return nlif_catch_multi(h); 320 } 321 322 /** Returns socket descriptor for the netlink socket 323 * 324 * \param nlif_handle A pointer to a ::nlif_handle created 325 * \return The fd or -1 if there's an error 326 */ 327 int nlif_fd(struct nlif_handle *h) 328 { 329 assert(h != NULL); 330 331 if (h->rtnl_handle) 332 return h->rtnl_handle->rtnl_fd; 333 334 return -1; 335 } 336