[AFS]: Add support for the CB.GetCapabilities operation.
[linux-2.6/kmemtrace.git] / fs / afs / use-rtnetlink.c
blob82f0daa28970ea92d97e1041b4a34c5a86b79e3e
1 /* RTNETLINK client
3 * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved.
4 * Written by David Howells (dhowells@redhat.com)
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License
8 * as published by the Free Software Foundation; either version
9 * 2 of the License, or (at your option) any later version.
11 #include <linux/netlink.h>
12 #include <linux/rtnetlink.h>
13 #include <linux/if_addr.h>
14 #include <linux/if_arp.h>
15 #include <linux/inetdevice.h>
16 #include <net/netlink.h>
17 #include "internal.h"
19 struct afs_rtm_desc {
20 struct socket *nlsock;
21 struct afs_interface *bufs;
22 u8 *mac;
23 size_t nbufs;
24 size_t maxbufs;
25 void *data;
26 ssize_t datalen;
27 size_t datamax;
28 int msg_seq;
29 unsigned mac_index;
30 bool wantloopback;
31 int (*parse)(struct afs_rtm_desc *, struct nlmsghdr *);
35 * parse an RTM_GETADDR response
37 static int afs_rtm_getaddr_parse(struct afs_rtm_desc *desc,
38 struct nlmsghdr *nlhdr)
40 struct afs_interface *this;
41 struct ifaddrmsg *ifa;
42 struct rtattr *rtattr;
43 const char *name;
44 size_t len;
46 ifa = (struct ifaddrmsg *) NLMSG_DATA(nlhdr);
48 _enter("{ix=%d,af=%d}", ifa->ifa_index, ifa->ifa_family);
50 if (ifa->ifa_family != AF_INET) {
51 _leave(" = 0 [family %d]", ifa->ifa_family);
52 return 0;
54 if (desc->nbufs >= desc->maxbufs) {
55 _leave(" = 0 [max %zu/%zu]", desc->nbufs, desc->maxbufs);
56 return 0;
59 this = &desc->bufs[desc->nbufs];
61 this->index = ifa->ifa_index;
62 this->netmask.s_addr = inet_make_mask(ifa->ifa_prefixlen);
63 this->mtu = 0;
65 rtattr = NLMSG_DATA(nlhdr) + NLMSG_ALIGN(sizeof(struct ifaddrmsg));
66 len = NLMSG_PAYLOAD(nlhdr, sizeof(struct ifaddrmsg));
68 name = "unknown";
69 for (; RTA_OK(rtattr, len); rtattr = RTA_NEXT(rtattr, len)) {
70 switch (rtattr->rta_type) {
71 case IFA_ADDRESS:
72 memcpy(&this->address, RTA_DATA(rtattr), 4);
73 break;
74 case IFA_LABEL:
75 name = RTA_DATA(rtattr);
76 break;
80 _debug("%s: "NIPQUAD_FMT"/"NIPQUAD_FMT,
81 name, NIPQUAD(this->address), NIPQUAD(this->netmask));
83 desc->nbufs++;
84 _leave(" = 0");
85 return 0;
89 * parse an RTM_GETLINK response for MTUs
91 static int afs_rtm_getlink_if_parse(struct afs_rtm_desc *desc,
92 struct nlmsghdr *nlhdr)
94 struct afs_interface *this;
95 struct ifinfomsg *ifi;
96 struct rtattr *rtattr;
97 const char *name;
98 size_t len, loop;
100 ifi = (struct ifinfomsg *) NLMSG_DATA(nlhdr);
102 _enter("{ix=%d}", ifi->ifi_index);
104 for (loop = 0; loop < desc->nbufs; loop++) {
105 this = &desc->bufs[loop];
106 if (this->index == ifi->ifi_index)
107 goto found;
110 _leave(" = 0 [no match]");
111 return 0;
113 found:
114 if (ifi->ifi_type == ARPHRD_LOOPBACK && !desc->wantloopback) {
115 _leave(" = 0 [loopback]");
116 return 0;
119 rtattr = NLMSG_DATA(nlhdr) + NLMSG_ALIGN(sizeof(struct ifinfomsg));
120 len = NLMSG_PAYLOAD(nlhdr, sizeof(struct ifinfomsg));
122 name = "unknown";
123 for (; RTA_OK(rtattr, len); rtattr = RTA_NEXT(rtattr, len)) {
124 switch (rtattr->rta_type) {
125 case IFLA_MTU:
126 memcpy(&this->mtu, RTA_DATA(rtattr), 4);
127 break;
128 case IFLA_IFNAME:
129 name = RTA_DATA(rtattr);
130 break;
134 _debug("%s: "NIPQUAD_FMT"/"NIPQUAD_FMT" mtu %u",
135 name, NIPQUAD(this->address), NIPQUAD(this->netmask),
136 this->mtu);
138 _leave(" = 0");
139 return 0;
143 * parse an RTM_GETLINK response for the MAC address belonging to the lowest
144 * non-internal interface
146 static int afs_rtm_getlink_mac_parse(struct afs_rtm_desc *desc,
147 struct nlmsghdr *nlhdr)
149 struct ifinfomsg *ifi;
150 struct rtattr *rtattr;
151 const char *name;
152 size_t remain, len;
153 bool set;
155 ifi = (struct ifinfomsg *) NLMSG_DATA(nlhdr);
157 _enter("{ix=%d}", ifi->ifi_index);
159 if (ifi->ifi_index >= desc->mac_index) {
160 _leave(" = 0 [high]");
161 return 0;
163 if (ifi->ifi_type == ARPHRD_LOOPBACK) {
164 _leave(" = 0 [loopback]");
165 return 0;
168 rtattr = NLMSG_DATA(nlhdr) + NLMSG_ALIGN(sizeof(struct ifinfomsg));
169 remain = NLMSG_PAYLOAD(nlhdr, sizeof(struct ifinfomsg));
171 name = "unknown";
172 set = false;
173 for (; RTA_OK(rtattr, remain); rtattr = RTA_NEXT(rtattr, remain)) {
174 switch (rtattr->rta_type) {
175 case IFLA_ADDRESS:
176 len = RTA_PAYLOAD(rtattr);
177 memcpy(desc->mac, RTA_DATA(rtattr),
178 min_t(size_t, len, 6));
179 desc->mac_index = ifi->ifi_index;
180 set = true;
181 break;
182 case IFLA_IFNAME:
183 name = RTA_DATA(rtattr);
184 break;
188 if (set)
189 _debug("%s: %02x:%02x:%02x:%02x:%02x:%02x",
190 name,
191 desc->mac[0], desc->mac[1], desc->mac[2],
192 desc->mac[3], desc->mac[4], desc->mac[5]);
194 _leave(" = 0");
195 return 0;
199 * read the rtnetlink response and pass to parsing routine
201 static int afs_read_rtm(struct afs_rtm_desc *desc)
203 struct nlmsghdr *nlhdr, tmphdr;
204 struct msghdr msg;
205 struct kvec iov[1];
206 void *data;
207 bool last = false;
208 int len, ret, remain;
210 _enter("");
212 do {
213 /* first of all peek to see how big the packet is */
214 memset(&msg, 0, sizeof(msg));
215 iov[0].iov_base = &tmphdr;
216 iov[0].iov_len = sizeof(tmphdr);
217 len = kernel_recvmsg(desc->nlsock, &msg, iov, 1,
218 sizeof(tmphdr), MSG_PEEK | MSG_TRUNC);
219 if (len < 0) {
220 _leave(" = %d [peek]", len);
221 return len;
223 if (len == 0)
224 continue;
225 if (len < sizeof(tmphdr) || len < NLMSG_PAYLOAD(&tmphdr, 0)) {
226 _leave(" = -EMSGSIZE");
227 return -EMSGSIZE;
230 if (desc->datamax < len) {
231 kfree(desc->data);
232 desc->data = NULL;
233 data = kmalloc(len, GFP_KERNEL);
234 if (!data)
235 return -ENOMEM;
236 desc->data = data;
238 desc->datamax = len;
240 /* read all the data from this packet */
241 iov[0].iov_base = desc->data;
242 iov[0].iov_len = desc->datamax;
243 desc->datalen = kernel_recvmsg(desc->nlsock, &msg, iov, 1,
244 desc->datamax, 0);
245 if (desc->datalen < 0) {
246 _leave(" = %ld [recv]", desc->datalen);
247 return desc->datalen;
250 nlhdr = desc->data;
252 /* check if the header is valid */
253 if (!NLMSG_OK(nlhdr, desc->datalen) ||
254 nlhdr->nlmsg_type == NLMSG_ERROR) {
255 _leave(" = -EIO");
256 return -EIO;
259 /* see if this is the last message */
260 if (nlhdr->nlmsg_type == NLMSG_DONE ||
261 !(nlhdr->nlmsg_flags & NLM_F_MULTI))
262 last = true;
264 /* parse the bits we got this time */
265 nlmsg_for_each_msg(nlhdr, desc->data, desc->datalen, remain) {
266 ret = desc->parse(desc, nlhdr);
267 if (ret < 0) {
268 _leave(" = %d [parse]", ret);
269 return ret;
273 } while (!last);
275 _leave(" = 0");
276 return 0;
280 * list the interface bound addresses to get the address and netmask
282 static int afs_rtm_getaddr(struct afs_rtm_desc *desc)
284 struct msghdr msg;
285 struct kvec iov[1];
286 int ret;
288 struct {
289 struct nlmsghdr nl_msg __attribute__((aligned(NLMSG_ALIGNTO)));
290 struct ifaddrmsg addr_msg __attribute__((aligned(NLMSG_ALIGNTO)));
291 } request;
293 _enter("");
295 memset(&request, 0, sizeof(request));
297 request.nl_msg.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifaddrmsg));
298 request.nl_msg.nlmsg_type = RTM_GETADDR;
299 request.nl_msg.nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP;
300 request.nl_msg.nlmsg_seq = desc->msg_seq++;
301 request.nl_msg.nlmsg_pid = 0;
303 memset(&msg, 0, sizeof(msg));
304 iov[0].iov_base = &request;
305 iov[0].iov_len = sizeof(request);
307 ret = kernel_sendmsg(desc->nlsock, &msg, iov, 1, iov[0].iov_len);
308 _leave(" = %d", ret);
309 return ret;
313 * list the interface link statuses to get the MTUs
315 static int afs_rtm_getlink(struct afs_rtm_desc *desc)
317 struct msghdr msg;
318 struct kvec iov[1];
319 int ret;
321 struct {
322 struct nlmsghdr nl_msg __attribute__((aligned(NLMSG_ALIGNTO)));
323 struct ifinfomsg link_msg __attribute__((aligned(NLMSG_ALIGNTO)));
324 } request;
326 _enter("");
328 memset(&request, 0, sizeof(request));
330 request.nl_msg.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg));
331 request.nl_msg.nlmsg_type = RTM_GETLINK;
332 request.nl_msg.nlmsg_flags = NLM_F_REQUEST | NLM_F_ROOT;
333 request.nl_msg.nlmsg_seq = desc->msg_seq++;
334 request.nl_msg.nlmsg_pid = 0;
336 memset(&msg, 0, sizeof(msg));
337 iov[0].iov_base = &request;
338 iov[0].iov_len = sizeof(request);
340 ret = kernel_sendmsg(desc->nlsock, &msg, iov, 1, iov[0].iov_len);
341 _leave(" = %d", ret);
342 return ret;
346 * cull any interface records for which there isn't an MTU value
348 static void afs_cull_interfaces(struct afs_rtm_desc *desc)
350 struct afs_interface *bufs = desc->bufs;
351 size_t nbufs = desc->nbufs;
352 int loop, point = 0;
354 _enter("{%zu}", nbufs);
356 for (loop = 0; loop < nbufs; loop++) {
357 if (desc->bufs[loop].mtu != 0) {
358 if (loop != point) {
359 ASSERTCMP(loop, >, point);
360 bufs[point] = bufs[loop];
362 point++;
366 desc->nbufs = point;
367 _leave(" [%zu/%zu]", desc->nbufs, nbufs);
371 * get a list of this system's interface IPv4 addresses, netmasks and MTUs
372 * - returns the number of interface records in the buffer
374 int afs_get_ipv4_interfaces(struct afs_interface *bufs, size_t maxbufs,
375 bool wantloopback)
377 struct afs_rtm_desc desc;
378 int ret, loop;
380 _enter("");
382 memset(&desc, 0, sizeof(desc));
383 desc.bufs = bufs;
384 desc.maxbufs = maxbufs;
385 desc.wantloopback = wantloopback;
387 ret = sock_create_kern(AF_NETLINK, SOCK_DGRAM, NETLINK_ROUTE,
388 &desc.nlsock);
389 if (ret < 0) {
390 _leave(" = %d [sock]", ret);
391 return ret;
394 /* issue RTM_GETADDR */
395 desc.parse = afs_rtm_getaddr_parse;
396 ret = afs_rtm_getaddr(&desc);
397 if (ret < 0)
398 goto error;
399 ret = afs_read_rtm(&desc);
400 if (ret < 0)
401 goto error;
403 /* issue RTM_GETLINK */
404 desc.parse = afs_rtm_getlink_if_parse;
405 ret = afs_rtm_getlink(&desc);
406 if (ret < 0)
407 goto error;
408 ret = afs_read_rtm(&desc);
409 if (ret < 0)
410 goto error;
412 afs_cull_interfaces(&desc);
413 ret = desc.nbufs;
415 for (loop = 0; loop < ret; loop++)
416 _debug("[%d] "NIPQUAD_FMT"/"NIPQUAD_FMT" mtu %u",
417 bufs[loop].index,
418 NIPQUAD(bufs[loop].address),
419 NIPQUAD(bufs[loop].netmask),
420 bufs[loop].mtu);
422 error:
423 kfree(desc.data);
424 sock_release(desc.nlsock);
425 _leave(" = %d", ret);
426 return ret;
430 * get a MAC address from a random ethernet interface that has a real one
431 * - the buffer should be 6 bytes in size
433 int afs_get_MAC_address(u8 mac[6])
435 struct afs_rtm_desc desc;
436 int ret;
438 _enter("");
440 memset(&desc, 0, sizeof(desc));
441 desc.mac = mac;
442 desc.mac_index = UINT_MAX;
444 ret = sock_create_kern(AF_NETLINK, SOCK_DGRAM, NETLINK_ROUTE,
445 &desc.nlsock);
446 if (ret < 0) {
447 _leave(" = %d [sock]", ret);
448 return ret;
451 /* issue RTM_GETLINK */
452 desc.parse = afs_rtm_getlink_mac_parse;
453 ret = afs_rtm_getlink(&desc);
454 if (ret < 0)
455 goto error;
456 ret = afs_read_rtm(&desc);
457 if (ret < 0)
458 goto error;
460 if (desc.mac_index < UINT_MAX) {
461 /* got a MAC address */
462 _debug("[%d] %02x:%02x:%02x:%02x:%02x:%02x",
463 desc.mac_index,
464 mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
465 } else {
466 ret = -ENONET;
469 error:
470 sock_release(desc.nlsock);
471 _leave(" = %d", ret);
472 return ret;