netlink.c revision 8d520ff1dc2da35cdca849e982051b86468016d8
18d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt/* 28d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt * Netlink helper functions for driver wrappers 38d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt * Copyright (c) 2002-2009, Jouni Malinen <j@w1.fi> 48d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt * 58d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt * This program is free software; you can redistribute it and/or modify 68d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt * it under the terms of the GNU General Public License version 2 as 78d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt * published by the Free Software Foundation. 88d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt * 98d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt * Alternatively, this software may be distributed under the terms of BSD 108d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt * license. 118d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt * 128d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt * See README and COPYING for more details. 138d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt */ 148d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt 158d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt#include "includes.h" 168d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt 178d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt#include "common.h" 188d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt#include "eloop.h" 198d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt#include "priv_netlink.h" 208d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt#include "netlink.h" 218d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt 228d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt 238d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidtstruct netlink_data { 248d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt struct netlink_config *cfg; 258d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt int sock; 268d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt}; 278d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt 288d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt 298d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidtstatic void netlink_receive_link(struct netlink_data *netlink, 308d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt void (*cb)(void *ctx, struct ifinfomsg *ifi, 318d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt u8 *buf, size_t len), 328d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt struct nlmsghdr *h) 338d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt{ 348d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt if (cb == NULL || NLMSG_PAYLOAD(h, 0) < sizeof(struct ifinfomsg)) 358d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt return; 368d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt cb(netlink->cfg->ctx, NLMSG_DATA(h), 378d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt NLMSG_DATA(h) + NLMSG_ALIGN(sizeof(struct ifinfomsg)), 388d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt NLMSG_PAYLOAD(h, sizeof(struct ifinfomsg))); 398d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt} 408d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt 418d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt 428d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidtstatic void netlink_receive(int sock, void *eloop_ctx, void *sock_ctx) 438d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt{ 448d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt struct netlink_data *netlink = eloop_ctx; 458d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt char buf[8192]; 468d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt int left; 478d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt struct sockaddr_nl from; 488d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt socklen_t fromlen; 498d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt struct nlmsghdr *h; 508d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt int max_events = 10; 518d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt 528d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidttry_again: 538d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt fromlen = sizeof(from); 548d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt left = recvfrom(sock, buf, sizeof(buf), MSG_DONTWAIT, 558d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt (struct sockaddr *) &from, &fromlen); 568d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt if (left < 0) { 578d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt if (errno != EINTR && errno != EAGAIN) 588d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt wpa_printf(MSG_INFO, "netlink: recvfrom failed: %s", 598d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt strerror(errno)); 608d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt return; 618d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt } 628d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt 638d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt h = (struct nlmsghdr *) buf; 648d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt while (NLMSG_OK(h, left)) { 658d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt switch (h->nlmsg_type) { 668d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt case RTM_NEWLINK: 678d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt netlink_receive_link(netlink, netlink->cfg->newlink_cb, 688d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt h); 698d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt break; 708d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt case RTM_DELLINK: 718d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt netlink_receive_link(netlink, netlink->cfg->dellink_cb, 728d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt h); 738d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt break; 748d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt } 758d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt 768d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt h = NLMSG_NEXT(h, left); 778d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt } 788d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt 798d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt if (left > 0) { 808d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt wpa_printf(MSG_DEBUG, "netlink: %d extra bytes in the end of " 818d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt "netlink message", left); 828d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt } 838d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt 848d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt if (--max_events > 0) { 858d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt /* 868d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt * Try to receive all events in one eloop call in order to 878d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt * limit race condition on cases where AssocInfo event, Assoc 888d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt * event, and EAPOL frames are received more or less at the 898d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt * same time. We want to process the event messages first 908d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt * before starting EAPOL processing. 918d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt */ 928d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt goto try_again; 938d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt } 948d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt} 958d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt 968d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt 978d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidtstruct netlink_data * netlink_init(struct netlink_config *cfg) 988d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt{ 998d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt struct netlink_data *netlink; 1008d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt struct sockaddr_nl local; 1018d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt 1028d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt netlink = os_zalloc(sizeof(*netlink)); 1038d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt if (netlink == NULL) 1048d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt return NULL; 1058d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt 1068d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt netlink->cfg = cfg; 1078d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt 1088d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt netlink->sock = socket(PF_NETLINK, SOCK_RAW, NETLINK_ROUTE); 1098d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt if (netlink->sock < 0) { 1108d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt wpa_printf(MSG_ERROR, "netlink: Failed to open netlink " 1118d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt "socket: %s", strerror(errno)); 1128d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt netlink_deinit(netlink); 1138d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt return NULL; 1148d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt } 1158d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt 1168d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt os_memset(&local, 0, sizeof(local)); 1178d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt local.nl_family = AF_NETLINK; 1188d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt local.nl_groups = RTMGRP_LINK; 1198d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt if (bind(netlink->sock, (struct sockaddr *) &local, sizeof(local)) < 0) 1208d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt { 1218d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt wpa_printf(MSG_ERROR, "netlink: Failed to bind netlink " 1228d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt "socket: %s", strerror(errno)); 1238d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt netlink_deinit(netlink); 1248d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt return NULL; 1258d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt } 1268d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt 1278d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt eloop_register_read_sock(netlink->sock, netlink_receive, netlink, 1288d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt NULL); 1298d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt 1308d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt return netlink; 1318d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt} 1328d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt 1338d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt 1348d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidtvoid netlink_deinit(struct netlink_data *netlink) 1358d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt{ 1368d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt if (netlink == NULL) 1378d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt return; 1388d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt if (netlink->sock >= 0) { 1398d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt eloop_unregister_read_sock(netlink->sock); 1408d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt close(netlink->sock); 1418d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt } 1428d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt os_free(netlink->cfg); 1438d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt os_free(netlink); 1448d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt} 1458d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt 1468d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidtint netlink_send_oper_ifla(struct netlink_data *netlink, int ifindex, 1478d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt int linkmode, int operstate) 1488d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt{ 1498d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt struct { 1508d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt struct nlmsghdr hdr; 1518d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt struct ifinfomsg ifinfo; 1528d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt char opts[16]; 1538d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt } req; 1548d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt struct rtattr *rta; 1558d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt static int nl_seq; 1568d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt ssize_t ret; 1578d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt 1588d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt os_memset(&req, 0, sizeof(req)); 1598d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt 1608d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt req.hdr.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg)); 1618d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt req.hdr.nlmsg_type = RTM_SETLINK; 1628d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt req.hdr.nlmsg_flags = NLM_F_REQUEST; 1638d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt req.hdr.nlmsg_seq = ++nl_seq; 1648d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt req.hdr.nlmsg_pid = 0; 1658d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt 1668d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt req.ifinfo.ifi_family = AF_UNSPEC; 1678d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt req.ifinfo.ifi_type = 0; 1688d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt req.ifinfo.ifi_index = ifindex; 1698d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt req.ifinfo.ifi_flags = 0; 1708d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt req.ifinfo.ifi_change = 0; 1718d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt 1728d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt if (linkmode != -1) { 1738d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt rta = aliasing_hide_typecast( 1748d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt ((char *) &req + NLMSG_ALIGN(req.hdr.nlmsg_len)), 1758d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt struct rtattr); 1768d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt rta->rta_type = IFLA_LINKMODE; 1778d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt rta->rta_len = RTA_LENGTH(sizeof(char)); 1788d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt *((char *) RTA_DATA(rta)) = linkmode; 1798d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt req.hdr.nlmsg_len = NLMSG_ALIGN(req.hdr.nlmsg_len) + 1808d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt RTA_LENGTH(sizeof(char)); 1818d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt } 1828d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt if (operstate != -1) { 1838d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt rta = aliasing_hide_typecast( 1848d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt ((char *) &req + NLMSG_ALIGN(req.hdr.nlmsg_len)), 1858d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt struct rtattr); 1868d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt rta->rta_type = IFLA_OPERSTATE; 1878d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt rta->rta_len = RTA_LENGTH(sizeof(char)); 1888d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt *((char *) RTA_DATA(rta)) = operstate; 1898d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt req.hdr.nlmsg_len = NLMSG_ALIGN(req.hdr.nlmsg_len) + 1908d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt RTA_LENGTH(sizeof(char)); 1918d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt } 1928d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt 1938d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt wpa_printf(MSG_DEBUG, "netlink: Operstate: linkmode=%d, operstate=%d", 1948d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt linkmode, operstate); 1958d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt 1968d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt ret = send(netlink->sock, &req, req.hdr.nlmsg_len, 0); 1978d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt if (ret < 0) { 1988d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt wpa_printf(MSG_DEBUG, "netlink: Sending operstate IFLA " 1998d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt "failed: %s (assume operstate is not supported)", 2008d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt strerror(errno)); 2018d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt } 2028d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt 2038d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt return ret < 0 ? -1 : 0; 2048d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt} 205