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