$search
00001 /* 00002 * Netlink helper functions for driver wrappers 00003 * Copyright (c) 2002-2009, Jouni Malinen <j@w1.fi> 00004 * 00005 * This program is free software; you can redistribute it and/or modify 00006 * it under the terms of the GNU General Public License version 2 as 00007 * published by the Free Software Foundation. 00008 * 00009 * Alternatively, this software may be distributed under the terms of BSD 00010 * license. 00011 * 00012 * See README and COPYING for more details. 00013 */ 00014 00015 #include "includes.h" 00016 00017 #include "common.h" 00018 #include "eloop.h" 00019 #include "priv_netlink.h" 00020 #include "netlink.h" 00021 00022 00023 struct netlink_data { 00024 struct netlink_config *cfg; 00025 int sock; 00026 }; 00027 00028 00029 static void netlink_receive_link(struct netlink_data *netlink, 00030 void (*cb)(void *ctx, struct ifinfomsg *ifi, 00031 u8 *buf, size_t len), 00032 struct nlmsghdr *h) 00033 { 00034 if (cb == NULL || NLMSG_PAYLOAD(h, 0) < sizeof(struct ifinfomsg)) 00035 return; 00036 cb(netlink->cfg->ctx, NLMSG_DATA(h), 00037 NLMSG_DATA(h) + NLMSG_ALIGN(sizeof(struct ifinfomsg)), 00038 NLMSG_PAYLOAD(h, sizeof(struct ifinfomsg))); 00039 } 00040 00041 00042 static void netlink_receive(int sock, void *eloop_ctx, void *sock_ctx) 00043 { 00044 struct netlink_data *netlink = eloop_ctx; 00045 char buf[8192]; 00046 int left; 00047 struct sockaddr_nl from; 00048 socklen_t fromlen; 00049 struct nlmsghdr *h; 00050 int max_events = 10; 00051 00052 try_again: 00053 fromlen = sizeof(from); 00054 left = recvfrom(sock, buf, sizeof(buf), MSG_DONTWAIT, 00055 (struct sockaddr *) &from, &fromlen); 00056 if (left < 0) { 00057 if (errno != EINTR && errno != EAGAIN) 00058 wpa_printf(MSG_INFO, "netlink: recvfrom failed: %s", 00059 strerror(errno)); 00060 return; 00061 } 00062 00063 h = (struct nlmsghdr *) buf; 00064 while (NLMSG_OK(h, left)) { 00065 switch (h->nlmsg_type) { 00066 case RTM_NEWLINK: 00067 netlink_receive_link(netlink, netlink->cfg->newlink_cb, 00068 h); 00069 break; 00070 case RTM_DELLINK: 00071 netlink_receive_link(netlink, netlink->cfg->dellink_cb, 00072 h); 00073 break; 00074 } 00075 00076 h = NLMSG_NEXT(h, left); 00077 } 00078 00079 if (left > 0) { 00080 wpa_printf(MSG_DEBUG, "netlink: %d extra bytes in the end of " 00081 "netlink message", left); 00082 } 00083 00084 if (--max_events > 0) { 00085 /* 00086 * Try to receive all events in one eloop call in order to 00087 * limit race condition on cases where AssocInfo event, Assoc 00088 * event, and EAPOL frames are received more or less at the 00089 * same time. We want to process the event messages first 00090 * before starting EAPOL processing. 00091 */ 00092 goto try_again; 00093 } 00094 } 00095 00096 00097 struct netlink_data * netlink_init(struct netlink_config *cfg) 00098 { 00099 struct netlink_data *netlink; 00100 struct sockaddr_nl local; 00101 00102 netlink = os_zalloc(sizeof(*netlink)); 00103 if (netlink == NULL) 00104 return NULL; 00105 00106 netlink->cfg = cfg; 00107 00108 netlink->sock = socket(PF_NETLINK, SOCK_RAW, NETLINK_ROUTE); 00109 if (netlink->sock < 0) { 00110 wpa_printf(MSG_ERROR, "netlink: Failed to open netlink " 00111 "socket: %s", strerror(errno)); 00112 netlink_deinit(netlink); 00113 return NULL; 00114 } 00115 00116 os_memset(&local, 0, sizeof(local)); 00117 local.nl_family = AF_NETLINK; 00118 local.nl_groups = RTMGRP_LINK; 00119 if (bind(netlink->sock, (struct sockaddr *) &local, sizeof(local)) < 0) 00120 { 00121 wpa_printf(MSG_ERROR, "netlink: Failed to bind netlink " 00122 "socket: %s", strerror(errno)); 00123 netlink_deinit(netlink); 00124 return NULL; 00125 } 00126 00127 eloop_register_read_sock(netlink->sock, netlink_receive, netlink, 00128 NULL); 00129 00130 return netlink; 00131 } 00132 00133 00134 void netlink_deinit(struct netlink_data *netlink) 00135 { 00136 if (netlink == NULL) 00137 return; 00138 if (netlink->sock >= 0) { 00139 eloop_unregister_read_sock(netlink->sock); 00140 close(netlink->sock); 00141 } 00142 os_free(netlink->cfg); 00143 os_free(netlink); 00144 } 00145 00146 int netlink_send_oper_ifla(struct netlink_data *netlink, int ifindex, 00147 int linkmode, int operstate) 00148 { 00149 struct { 00150 struct nlmsghdr hdr; 00151 struct ifinfomsg ifinfo; 00152 char opts[16]; 00153 } req; 00154 struct rtattr *rta; 00155 static int nl_seq; 00156 ssize_t ret; 00157 00158 os_memset(&req, 0, sizeof(req)); 00159 00160 req.hdr.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg)); 00161 req.hdr.nlmsg_type = RTM_SETLINK; 00162 req.hdr.nlmsg_flags = NLM_F_REQUEST; 00163 req.hdr.nlmsg_seq = ++nl_seq; 00164 req.hdr.nlmsg_pid = 0; 00165 00166 req.ifinfo.ifi_family = AF_UNSPEC; 00167 req.ifinfo.ifi_type = 0; 00168 req.ifinfo.ifi_index = ifindex; 00169 req.ifinfo.ifi_flags = 0; 00170 req.ifinfo.ifi_change = 0; 00171 00172 if (linkmode != -1) { 00173 rta = aliasing_hide_typecast( 00174 ((char *) &req + NLMSG_ALIGN(req.hdr.nlmsg_len)), 00175 struct rtattr); 00176 rta->rta_type = IFLA_LINKMODE; 00177 rta->rta_len = RTA_LENGTH(sizeof(char)); 00178 *((char *) RTA_DATA(rta)) = linkmode; 00179 req.hdr.nlmsg_len = NLMSG_ALIGN(req.hdr.nlmsg_len) + 00180 RTA_LENGTH(sizeof(char)); 00181 } 00182 if (operstate != -1) { 00183 rta = aliasing_hide_typecast( 00184 ((char *) &req + NLMSG_ALIGN(req.hdr.nlmsg_len)), 00185 struct rtattr); 00186 rta->rta_type = IFLA_OPERSTATE; 00187 rta->rta_len = RTA_LENGTH(sizeof(char)); 00188 *((char *) RTA_DATA(rta)) = operstate; 00189 req.hdr.nlmsg_len = NLMSG_ALIGN(req.hdr.nlmsg_len) + 00190 RTA_LENGTH(sizeof(char)); 00191 } 00192 00193 wpa_printf(MSG_DEBUG, "netlink: Operstate: linkmode=%d, operstate=%d", 00194 linkmode, operstate); 00195 00196 ret = send(netlink->sock, &req, req.hdr.nlmsg_len, 0); 00197 if (ret < 0) { 00198 wpa_printf(MSG_DEBUG, "netlink: Sending operstate IFLA " 00199 "failed: %s (assume operstate is not supported)", 00200 strerror(errno)); 00201 } 00202 00203 return ret < 0 ? -1 : 0; 00204 }