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