1/* 2 * Netlink helper functions for driver wrappers 3 * Copyright (c) 2002-2009, Jouni Malinen <j@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 17struct netlink_data { 18 struct netlink_config *cfg; 19 int sock; 20}; 21 22 23static 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 36static 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 46try_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 91struct 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 128void 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 140int netlink_send_oper_ifla(struct netlink_data *netlink, int ifindex, 141 int linkmode, int operstate) 142{ 143 struct { 144 struct nlmsghdr hdr; 145 struct ifinfomsg ifinfo; 146 char opts[16]; 147 } req; 148 struct rtattr *rta; 149 static int nl_seq; 150 ssize_t ret; 151 152 os_memset(&req, 0, sizeof(req)); 153 154 req.hdr.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg)); 155 req.hdr.nlmsg_type = RTM_SETLINK; 156 req.hdr.nlmsg_flags = NLM_F_REQUEST; 157 req.hdr.nlmsg_seq = ++nl_seq; 158 req.hdr.nlmsg_pid = 0; 159 160 req.ifinfo.ifi_family = AF_UNSPEC; 161 req.ifinfo.ifi_type = 0; 162 req.ifinfo.ifi_index = ifindex; 163 req.ifinfo.ifi_flags = 0; 164 req.ifinfo.ifi_change = 0; 165 166 if (linkmode != -1) { 167 rta = aliasing_hide_typecast( 168 ((char *) &req + NLMSG_ALIGN(req.hdr.nlmsg_len)), 169 struct rtattr); 170 rta->rta_type = IFLA_LINKMODE; 171 rta->rta_len = RTA_LENGTH(sizeof(char)); 172 *((char *) RTA_DATA(rta)) = linkmode; 173 req.hdr.nlmsg_len = NLMSG_ALIGN(req.hdr.nlmsg_len) + 174 RTA_LENGTH(sizeof(char)); 175 } 176 if (operstate != -1) { 177 rta = aliasing_hide_typecast( 178 ((char *) &req + NLMSG_ALIGN(req.hdr.nlmsg_len)), 179 struct rtattr); 180 rta->rta_type = IFLA_OPERSTATE; 181 rta->rta_len = RTA_LENGTH(sizeof(char)); 182 *((char *) RTA_DATA(rta)) = operstate; 183 req.hdr.nlmsg_len = NLMSG_ALIGN(req.hdr.nlmsg_len) + 184 RTA_LENGTH(sizeof(char)); 185 } 186 187 wpa_printf(MSG_DEBUG, "netlink: Operstate: linkmode=%d, operstate=%d", 188 linkmode, operstate); 189 190 ret = send(netlink->sock, &req, req.hdr.nlmsg_len, 0); 191 if (ret < 0) { 192 wpa_printf(MSG_DEBUG, "netlink: Sending operstate IFLA " 193 "failed: %s (assume operstate is not supported)", 194 strerror(errno)); 195 } 196 197 return ret < 0 ? -1 : 0; 198} 199