1c1d255d3SCy Schubert /* 2c1d255d3SCy Schubert * Netlink helper functions for driver wrappers 3c1d255d3SCy Schubert * Copyright (c) 2002-2014, Jouni Malinen <j@w1.fi> 4c1d255d3SCy Schubert * 5c1d255d3SCy Schubert * This software may be distributed under the terms of the BSD license. 6c1d255d3SCy Schubert * See README for more details. 7c1d255d3SCy Schubert */ 8c1d255d3SCy Schubert 9c1d255d3SCy Schubert #include "includes.h" 10c1d255d3SCy Schubert 11c1d255d3SCy Schubert #include "common.h" 12c1d255d3SCy Schubert #include "eloop.h" 13c1d255d3SCy Schubert #include "priv_netlink.h" 14c1d255d3SCy Schubert #include "netlink.h" 15c1d255d3SCy Schubert 16c1d255d3SCy Schubert 17c1d255d3SCy Schubert struct netlink_data { 18c1d255d3SCy Schubert struct netlink_config *cfg; 19c1d255d3SCy Schubert int sock; 20c1d255d3SCy Schubert }; 21c1d255d3SCy Schubert 22c1d255d3SCy Schubert 23c1d255d3SCy Schubert static void netlink_receive_link(struct netlink_data *netlink, 24c1d255d3SCy Schubert void (*cb)(void *ctx, struct ifinfomsg *ifi, 25c1d255d3SCy Schubert u8 *buf, size_t len), 26c1d255d3SCy Schubert struct nlmsghdr *h) 27c1d255d3SCy Schubert { 28c1d255d3SCy Schubert if (cb == NULL || NLMSG_PAYLOAD(h, 0) < sizeof(struct ifinfomsg)) 29c1d255d3SCy Schubert return; 30c1d255d3SCy Schubert cb(netlink->cfg->ctx, NLMSG_DATA(h), 31c1d255d3SCy Schubert (u8 *) NLMSG_DATA(h) + NLMSG_ALIGN(sizeof(struct ifinfomsg)), 32c1d255d3SCy Schubert NLMSG_PAYLOAD(h, sizeof(struct ifinfomsg))); 33c1d255d3SCy Schubert } 34c1d255d3SCy Schubert 35c1d255d3SCy Schubert 36c1d255d3SCy Schubert static void netlink_receive(int sock, void *eloop_ctx, void *sock_ctx) 37c1d255d3SCy Schubert { 38c1d255d3SCy Schubert struct netlink_data *netlink = eloop_ctx; 39c1d255d3SCy Schubert char buf[8192]; 40c1d255d3SCy Schubert int left; 41c1d255d3SCy Schubert struct sockaddr_nl from; 42c1d255d3SCy Schubert socklen_t fromlen; 43c1d255d3SCy Schubert struct nlmsghdr *h; 44c1d255d3SCy Schubert int max_events = 10; 45c1d255d3SCy Schubert 46c1d255d3SCy Schubert try_again: 47c1d255d3SCy Schubert fromlen = sizeof(from); 48c1d255d3SCy Schubert left = recvfrom(sock, buf, sizeof(buf), MSG_DONTWAIT, 49c1d255d3SCy Schubert (struct sockaddr *) &from, &fromlen); 50c1d255d3SCy Schubert if (left < 0) { 51c1d255d3SCy Schubert if (errno != EINTR && errno != EAGAIN) 52c1d255d3SCy Schubert wpa_printf(MSG_INFO, "netlink: recvfrom failed: %s", 53c1d255d3SCy Schubert strerror(errno)); 54c1d255d3SCy Schubert return; 55c1d255d3SCy Schubert } 56c1d255d3SCy Schubert 57c1d255d3SCy Schubert h = (struct nlmsghdr *) buf; 58c1d255d3SCy Schubert while (NLMSG_OK(h, left)) { 59c1d255d3SCy Schubert switch (h->nlmsg_type) { 60c1d255d3SCy Schubert case RTM_NEWLINK: 61c1d255d3SCy Schubert netlink_receive_link(netlink, netlink->cfg->newlink_cb, 62c1d255d3SCy Schubert h); 63c1d255d3SCy Schubert break; 64c1d255d3SCy Schubert case RTM_DELLINK: 65c1d255d3SCy Schubert netlink_receive_link(netlink, netlink->cfg->dellink_cb, 66c1d255d3SCy Schubert h); 67c1d255d3SCy Schubert break; 68c1d255d3SCy Schubert } 69c1d255d3SCy Schubert 70c1d255d3SCy Schubert h = NLMSG_NEXT(h, left); 71c1d255d3SCy Schubert } 72c1d255d3SCy Schubert 73c1d255d3SCy Schubert if (left > 0) { 74c1d255d3SCy Schubert wpa_printf(MSG_DEBUG, "netlink: %d extra bytes in the end of " 75c1d255d3SCy Schubert "netlink message", left); 76c1d255d3SCy Schubert } 77c1d255d3SCy Schubert 78c1d255d3SCy Schubert if (--max_events > 0) { 79c1d255d3SCy Schubert /* 80c1d255d3SCy Schubert * Try to receive all events in one eloop call in order to 81c1d255d3SCy Schubert * limit race condition on cases where AssocInfo event, Assoc 82c1d255d3SCy Schubert * event, and EAPOL frames are received more or less at the 83c1d255d3SCy Schubert * same time. We want to process the event messages first 84c1d255d3SCy Schubert * before starting EAPOL processing. 85c1d255d3SCy Schubert */ 86c1d255d3SCy Schubert goto try_again; 87c1d255d3SCy Schubert } 88c1d255d3SCy Schubert } 89c1d255d3SCy Schubert 90c1d255d3SCy Schubert 91c1d255d3SCy Schubert struct netlink_data * netlink_init(struct netlink_config *cfg) 92c1d255d3SCy Schubert { 93c1d255d3SCy Schubert struct netlink_data *netlink; 94c1d255d3SCy Schubert struct sockaddr_nl local; 95c1d255d3SCy Schubert 96c1d255d3SCy Schubert netlink = os_zalloc(sizeof(*netlink)); 97c1d255d3SCy Schubert if (netlink == NULL) 98c1d255d3SCy Schubert return NULL; 99c1d255d3SCy Schubert 100c1d255d3SCy Schubert netlink->sock = socket(PF_NETLINK, SOCK_RAW, NETLINK_ROUTE); 101c1d255d3SCy Schubert if (netlink->sock < 0) { 102c1d255d3SCy Schubert wpa_printf(MSG_ERROR, "netlink: Failed to open netlink " 103c1d255d3SCy Schubert "socket: %s", strerror(errno)); 104c1d255d3SCy Schubert netlink_deinit(netlink); 105c1d255d3SCy Schubert return NULL; 106c1d255d3SCy Schubert } 107c1d255d3SCy Schubert 108c1d255d3SCy Schubert os_memset(&local, 0, sizeof(local)); 109c1d255d3SCy Schubert local.nl_family = AF_NETLINK; 110c1d255d3SCy Schubert local.nl_groups = RTMGRP_LINK; 111c1d255d3SCy Schubert if (bind(netlink->sock, (struct sockaddr *) &local, sizeof(local)) < 0) 112c1d255d3SCy Schubert { 113c1d255d3SCy Schubert wpa_printf(MSG_ERROR, "netlink: Failed to bind netlink " 114c1d255d3SCy Schubert "socket: %s", strerror(errno)); 115c1d255d3SCy Schubert netlink_deinit(netlink); 116c1d255d3SCy Schubert return NULL; 117c1d255d3SCy Schubert } 118c1d255d3SCy Schubert 119c1d255d3SCy Schubert eloop_register_read_sock(netlink->sock, netlink_receive, netlink, 120c1d255d3SCy Schubert NULL); 121c1d255d3SCy Schubert 122c1d255d3SCy Schubert netlink->cfg = cfg; 123c1d255d3SCy Schubert 124c1d255d3SCy Schubert return netlink; 125c1d255d3SCy Schubert } 126c1d255d3SCy Schubert 127c1d255d3SCy Schubert 128c1d255d3SCy Schubert void netlink_deinit(struct netlink_data *netlink) 129c1d255d3SCy Schubert { 130c1d255d3SCy Schubert if (netlink == NULL) 131c1d255d3SCy Schubert return; 132c1d255d3SCy Schubert if (netlink->sock >= 0) { 133c1d255d3SCy Schubert eloop_unregister_read_sock(netlink->sock); 134c1d255d3SCy Schubert close(netlink->sock); 135c1d255d3SCy Schubert } 136c1d255d3SCy Schubert os_free(netlink->cfg); 137c1d255d3SCy Schubert os_free(netlink); 138c1d255d3SCy Schubert } 139c1d255d3SCy Schubert 140c1d255d3SCy Schubert 141c1d255d3SCy Schubert static const char * linkmode_str(int mode) 142c1d255d3SCy Schubert { 143c1d255d3SCy Schubert switch (mode) { 144c1d255d3SCy Schubert case -1: 145c1d255d3SCy Schubert return "no change"; 146c1d255d3SCy Schubert case 0: 147c1d255d3SCy Schubert return "kernel-control"; 148c1d255d3SCy Schubert case 1: 149c1d255d3SCy Schubert return "userspace-control"; 150*a90b9d01SCy Schubert default: 151c1d255d3SCy Schubert return "?"; 152c1d255d3SCy Schubert } 153*a90b9d01SCy Schubert } 154c1d255d3SCy Schubert 155c1d255d3SCy Schubert 156c1d255d3SCy Schubert static const char * operstate_str(int state) 157c1d255d3SCy Schubert { 158c1d255d3SCy Schubert switch (state) { 159c1d255d3SCy Schubert case -1: 160c1d255d3SCy Schubert return "no change"; 161c1d255d3SCy Schubert case IF_OPER_DORMANT: 162c1d255d3SCy Schubert return "IF_OPER_DORMANT"; 163c1d255d3SCy Schubert case IF_OPER_UP: 164c1d255d3SCy Schubert return "IF_OPER_UP"; 165*a90b9d01SCy Schubert default: 166c1d255d3SCy Schubert return "?"; 167c1d255d3SCy Schubert } 168*a90b9d01SCy Schubert } 169c1d255d3SCy Schubert 170c1d255d3SCy Schubert 171c1d255d3SCy Schubert int netlink_send_oper_ifla(struct netlink_data *netlink, int ifindex, 172c1d255d3SCy Schubert int linkmode, int operstate) 173c1d255d3SCy Schubert { 174c1d255d3SCy Schubert struct { 175c1d255d3SCy Schubert struct nlmsghdr hdr; 176c1d255d3SCy Schubert struct ifinfomsg ifinfo; 177c1d255d3SCy Schubert char opts[16]; 178c1d255d3SCy Schubert } req; 179c1d255d3SCy Schubert struct rtattr *rta; 180c1d255d3SCy Schubert static int nl_seq; 181c1d255d3SCy Schubert ssize_t ret; 182c1d255d3SCy Schubert 183c1d255d3SCy Schubert os_memset(&req, 0, sizeof(req)); 184c1d255d3SCy Schubert 185c1d255d3SCy Schubert req.hdr.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg)); 186c1d255d3SCy Schubert req.hdr.nlmsg_type = RTM_SETLINK; 187c1d255d3SCy Schubert req.hdr.nlmsg_flags = NLM_F_REQUEST; 188c1d255d3SCy Schubert req.hdr.nlmsg_seq = ++nl_seq; 189c1d255d3SCy Schubert req.hdr.nlmsg_pid = 0; 190c1d255d3SCy Schubert 191c1d255d3SCy Schubert req.ifinfo.ifi_family = AF_UNSPEC; 192c1d255d3SCy Schubert req.ifinfo.ifi_type = 0; 193c1d255d3SCy Schubert req.ifinfo.ifi_index = ifindex; 194c1d255d3SCy Schubert req.ifinfo.ifi_flags = 0; 195c1d255d3SCy Schubert req.ifinfo.ifi_change = 0; 196c1d255d3SCy Schubert 197c1d255d3SCy Schubert if (linkmode != -1) { 198c1d255d3SCy Schubert rta = aliasing_hide_typecast( 199c1d255d3SCy Schubert ((char *) &req + NLMSG_ALIGN(req.hdr.nlmsg_len)), 200c1d255d3SCy Schubert struct rtattr); 201c1d255d3SCy Schubert rta->rta_type = IFLA_LINKMODE; 202c1d255d3SCy Schubert rta->rta_len = RTA_LENGTH(sizeof(char)); 203c1d255d3SCy Schubert *((char *) RTA_DATA(rta)) = linkmode; 204c1d255d3SCy Schubert req.hdr.nlmsg_len += RTA_SPACE(sizeof(char)); 205c1d255d3SCy Schubert } 206c1d255d3SCy Schubert if (operstate != -1) { 207c1d255d3SCy Schubert rta = aliasing_hide_typecast( 208c1d255d3SCy Schubert ((char *) &req + NLMSG_ALIGN(req.hdr.nlmsg_len)), 209c1d255d3SCy Schubert struct rtattr); 210c1d255d3SCy Schubert rta->rta_type = IFLA_OPERSTATE; 211c1d255d3SCy Schubert rta->rta_len = RTA_LENGTH(sizeof(char)); 212c1d255d3SCy Schubert *((char *) RTA_DATA(rta)) = operstate; 213c1d255d3SCy Schubert req.hdr.nlmsg_len += RTA_SPACE(sizeof(char)); 214c1d255d3SCy Schubert } 215c1d255d3SCy Schubert 216c1d255d3SCy Schubert wpa_printf(MSG_DEBUG, "netlink: Operstate: ifindex=%d linkmode=%d (%s), operstate=%d (%s)", 217c1d255d3SCy Schubert ifindex, linkmode, linkmode_str(linkmode), 218c1d255d3SCy Schubert operstate, operstate_str(operstate)); 219c1d255d3SCy Schubert 220c1d255d3SCy Schubert ret = send(netlink->sock, &req, req.hdr.nlmsg_len, 0); 221c1d255d3SCy Schubert if (ret < 0) { 222c1d255d3SCy Schubert wpa_printf(MSG_DEBUG, "netlink: Sending operstate IFLA " 223c1d255d3SCy Schubert "failed: %s (assume operstate is not supported)", 224c1d255d3SCy Schubert strerror(errno)); 225c1d255d3SCy Schubert } 226c1d255d3SCy Schubert 227c1d255d3SCy Schubert return ret < 0 ? -1 : 0; 228c1d255d3SCy Schubert } 229