Branch data Line data Source code
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 : :
17 : : struct netlink_data {
18 : : struct netlink_config *cfg;
19 : : int sock;
20 : : };
21 : :
22 : :
23 : 2003 : 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 [ + - ][ - + ]: 2003 : if (cb == NULL || NLMSG_PAYLOAD(h, 0) < sizeof(struct ifinfomsg))
29 : 2003 : return;
30 : 2003 : cb(netlink->cfg->ctx, NLMSG_DATA(h),
31 : : (u8 *) NLMSG_DATA(h) + NLMSG_ALIGN(sizeof(struct ifinfomsg)),
32 : 2003 : NLMSG_PAYLOAD(h, sizeof(struct ifinfomsg)));
33 : : }
34 : :
35 : :
36 : 1646 : static void netlink_receive(int sock, void *eloop_ctx, void *sock_ctx)
37 : : {
38 : 1646 : 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 : 1646 : int max_events = 10;
45 : :
46 : : try_again:
47 : 3647 : fromlen = sizeof(from);
48 : 3647 : left = recvfrom(sock, buf, sizeof(buf), MSG_DONTWAIT,
49 : : (struct sockaddr *) &from, &fromlen);
50 [ + + ]: 3647 : if (left < 0) {
51 [ + - ][ - + ]: 1644 : if (errno != EINTR && errno != EAGAIN)
52 : 0 : wpa_printf(MSG_INFO, "netlink: recvfrom failed: %s",
53 : 0 : strerror(errno));
54 : 1646 : return;
55 : : }
56 : :
57 : 2003 : h = (struct nlmsghdr *) buf;
58 [ + + ][ + - ]: 4006 : while (NLMSG_OK(h, left)) {
[ + - ]
59 [ + + - ]: 2003 : switch (h->nlmsg_type) {
60 : : case RTM_NEWLINK:
61 : 1967 : netlink_receive_link(netlink, netlink->cfg->newlink_cb,
62 : : h);
63 : 1967 : break;
64 : : case RTM_DELLINK:
65 : 36 : netlink_receive_link(netlink, netlink->cfg->dellink_cb,
66 : : h);
67 : 36 : break;
68 : : }
69 : :
70 : 2003 : h = NLMSG_NEXT(h, left);
71 : : }
72 : :
73 [ - + ]: 2003 : if (left > 0) {
74 : 0 : wpa_printf(MSG_DEBUG, "netlink: %d extra bytes in the end of "
75 : : "netlink message", left);
76 : : }
77 : :
78 [ + + ]: 2003 : 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 : 2001 : goto try_again;
87 : : }
88 : : }
89 : :
90 : :
91 : 1 : struct netlink_data * netlink_init(struct netlink_config *cfg)
92 : : {
93 : : struct netlink_data *netlink;
94 : : struct sockaddr_nl local;
95 : :
96 : 1 : netlink = os_zalloc(sizeof(*netlink));
97 [ - + ]: 1 : if (netlink == NULL)
98 : 0 : return NULL;
99 : :
100 : 1 : netlink->sock = socket(PF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
101 [ - + ]: 1 : if (netlink->sock < 0) {
102 : 0 : wpa_printf(MSG_ERROR, "netlink: Failed to open netlink "
103 : 0 : "socket: %s", strerror(errno));
104 : 0 : netlink_deinit(netlink);
105 : 0 : return NULL;
106 : : }
107 : :
108 : 1 : os_memset(&local, 0, sizeof(local));
109 : 1 : local.nl_family = AF_NETLINK;
110 : 1 : local.nl_groups = RTMGRP_LINK;
111 [ - + ]: 1 : if (bind(netlink->sock, (struct sockaddr *) &local, sizeof(local)) < 0)
112 : : {
113 : 0 : wpa_printf(MSG_ERROR, "netlink: Failed to bind netlink "
114 : 0 : "socket: %s", strerror(errno));
115 : 0 : netlink_deinit(netlink);
116 : 0 : return NULL;
117 : : }
118 : :
119 : 1 : eloop_register_read_sock(netlink->sock, netlink_receive, netlink,
120 : : NULL);
121 : :
122 : 1 : netlink->cfg = cfg;
123 : :
124 : 1 : return netlink;
125 : : }
126 : :
127 : :
128 : 1 : void netlink_deinit(struct netlink_data *netlink)
129 : : {
130 [ - + ]: 1 : if (netlink == NULL)
131 : 1 : return;
132 [ + - ]: 1 : if (netlink->sock >= 0) {
133 : 1 : eloop_unregister_read_sock(netlink->sock);
134 : 1 : close(netlink->sock);
135 : : }
136 : 1 : os_free(netlink->cfg);
137 : 1 : os_free(netlink);
138 : : }
139 : :
140 : 346 : int 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 : 346 : os_memset(&req, 0, sizeof(req));
153 : :
154 : 346 : req.hdr.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg));
155 : 346 : req.hdr.nlmsg_type = RTM_SETLINK;
156 : 346 : req.hdr.nlmsg_flags = NLM_F_REQUEST;
157 : 346 : req.hdr.nlmsg_seq = ++nl_seq;
158 : 346 : req.hdr.nlmsg_pid = 0;
159 : :
160 : 346 : req.ifinfo.ifi_family = AF_UNSPEC;
161 : 346 : req.ifinfo.ifi_type = 0;
162 : 346 : req.ifinfo.ifi_index = ifindex;
163 : 346 : req.ifinfo.ifi_flags = 0;
164 : 346 : req.ifinfo.ifi_change = 0;
165 : :
166 [ + + ]: 346 : if (linkmode != -1) {
167 : 168 : rta = aliasing_hide_typecast(
168 : : ((char *) &req + NLMSG_ALIGN(req.hdr.nlmsg_len)),
169 : : struct rtattr);
170 : 168 : rta->rta_type = IFLA_LINKMODE;
171 : 168 : rta->rta_len = RTA_LENGTH(sizeof(char));
172 : 168 : *((char *) RTA_DATA(rta)) = linkmode;
173 : 168 : req.hdr.nlmsg_len = NLMSG_ALIGN(req.hdr.nlmsg_len) +
174 : : RTA_LENGTH(sizeof(char));
175 : : }
176 [ + - ]: 346 : if (operstate != -1) {
177 : 346 : rta = aliasing_hide_typecast(
178 : : ((char *) &req + NLMSG_ALIGN(req.hdr.nlmsg_len)),
179 : : struct rtattr);
180 : 346 : rta->rta_type = IFLA_OPERSTATE;
181 : 346 : rta->rta_len = RTA_LENGTH(sizeof(char));
182 : 346 : *((char *) RTA_DATA(rta)) = operstate;
183 : 346 : req.hdr.nlmsg_len = NLMSG_ALIGN(req.hdr.nlmsg_len) +
184 : : RTA_LENGTH(sizeof(char));
185 : : }
186 : :
187 : 346 : wpa_printf(MSG_DEBUG, "netlink: Operstate: linkmode=%d, operstate=%d",
188 : : linkmode, operstate);
189 : :
190 : 346 : ret = send(netlink->sock, &req, req.hdr.nlmsg_len, 0);
191 [ - + ]: 346 : if (ret < 0) {
192 : 0 : wpa_printf(MSG_DEBUG, "netlink: Sending operstate IFLA "
193 : : "failed: %s (assume operstate is not supported)",
194 : 0 : strerror(errno));
195 : : }
196 : :
197 [ - + ]: 346 : return ret < 0 ? -1 : 0;
198 : : }
|