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