[AFS]: Eliminate cmpxchg() usage in vlocation code.
[linux-2.6] / fs / afs / use-rtnetlink.c
1 /* RTNETLINK client
2  *
3  * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved.
4  * Written by David Howells (dhowells@redhat.com)
5  *
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.
10  */
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"
18
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 *);
32 };
33
34 /*
35  * parse an RTM_GETADDR response
36  */
37 static int afs_rtm_getaddr_parse(struct afs_rtm_desc *desc,
38                                  struct nlmsghdr *nlhdr)
39 {
40         struct afs_interface *this;
41         struct ifaddrmsg *ifa;
42         struct rtattr *rtattr;
43         const char *name;
44         size_t len;
45
46         ifa = (struct ifaddrmsg *) NLMSG_DATA(nlhdr);
47
48         _enter("{ix=%d,af=%d}", ifa->ifa_index, ifa->ifa_family);
49
50         if (ifa->ifa_family != AF_INET) {
51                 _leave(" = 0 [family %d]", ifa->ifa_family);
52                 return 0;
53         }
54         if (desc->nbufs >= desc->maxbufs) {
55                 _leave(" = 0 [max %zu/%zu]", desc->nbufs, desc->maxbufs);
56                 return 0;
57         }
58
59         this = &desc->bufs[desc->nbufs];
60
61         this->index = ifa->ifa_index;
62         this->netmask.s_addr = inet_make_mask(ifa->ifa_prefixlen);
63         this->mtu = 0;
64
65         rtattr = NLMSG_DATA(nlhdr) + NLMSG_ALIGN(sizeof(struct ifaddrmsg));
66         len = NLMSG_PAYLOAD(nlhdr, sizeof(struct ifaddrmsg));
67
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;
77                 }
78         }
79
80         _debug("%s: "NIPQUAD_FMT"/"NIPQUAD_FMT,
81                name, NIPQUAD(this->address), NIPQUAD(this->netmask));
82
83         desc->nbufs++;
84         _leave(" = 0");
85         return 0;
86 }
87
88 /*
89  * parse an RTM_GETLINK response for MTUs
90  */
91 static int afs_rtm_getlink_if_parse(struct afs_rtm_desc *desc,
92                                     struct nlmsghdr *nlhdr)
93 {
94         struct afs_interface *this;
95         struct ifinfomsg *ifi;
96         struct rtattr *rtattr;
97         const char *name;
98         size_t len, loop;
99
100         ifi = (struct ifinfomsg *) NLMSG_DATA(nlhdr);
101
102         _enter("{ix=%d}", ifi->ifi_index);
103
104         for (loop = 0; loop < desc->nbufs; loop++) {
105                 this = &desc->bufs[loop];
106                 if (this->index == ifi->ifi_index)
107                         goto found;
108         }
109
110         _leave(" = 0 [no match]");
111         return 0;
112
113 found:
114         if (ifi->ifi_type == ARPHRD_LOOPBACK && !desc->wantloopback) {
115                 _leave(" = 0 [loopback]");
116                 return 0;
117         }
118
119         rtattr = NLMSG_DATA(nlhdr) + NLMSG_ALIGN(sizeof(struct ifinfomsg));
120         len = NLMSG_PAYLOAD(nlhdr, sizeof(struct ifinfomsg));
121
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;
131                 }
132         }
133
134         _debug("%s: "NIPQUAD_FMT"/"NIPQUAD_FMT" mtu %u",
135                name, NIPQUAD(this->address), NIPQUAD(this->netmask),
136                this->mtu);
137
138         _leave(" = 0");
139         return 0;
140 }
141
142 /*
143  * parse an RTM_GETLINK response for the MAC address belonging to the lowest
144  * non-internal interface
145  */
146 static int afs_rtm_getlink_mac_parse(struct afs_rtm_desc *desc,
147                                      struct nlmsghdr *nlhdr)
148 {
149         struct ifinfomsg *ifi;
150         struct rtattr *rtattr;
151         const char *name;
152         size_t remain, len;
153         bool set;
154
155         ifi = (struct ifinfomsg *) NLMSG_DATA(nlhdr);
156
157         _enter("{ix=%d}", ifi->ifi_index);
158
159         if (ifi->ifi_index >= desc->mac_index) {
160                 _leave(" = 0 [high]");
161                 return 0;
162         }
163         if (ifi->ifi_type == ARPHRD_LOOPBACK) {
164                 _leave(" = 0 [loopback]");
165                 return 0;
166         }
167
168         rtattr = NLMSG_DATA(nlhdr) + NLMSG_ALIGN(sizeof(struct ifinfomsg));
169         remain = NLMSG_PAYLOAD(nlhdr, sizeof(struct ifinfomsg));
170
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;
185                 }
186         }
187
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]);
193
194         _leave(" = 0");
195         return 0;
196 }
197
198 /*
199  * read the rtnetlink response and pass to parsing routine
200  */
201 static int afs_read_rtm(struct afs_rtm_desc *desc)
202 {
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;
209
210         _enter("");
211
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;
222                 }
223                 if (len == 0)
224                         continue;
225                 if (len < sizeof(tmphdr) || len < NLMSG_PAYLOAD(&tmphdr, 0)) {
226                         _leave(" = -EMSGSIZE");
227                         return -EMSGSIZE;
228                 }
229
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;
237                 }
238                 desc->datamax = len;
239
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;
248                 }
249
250                 nlhdr = desc->data;
251
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;
257                 }
258
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;
263
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;
270                         }
271                 }
272
273         } while (!last);
274
275         _leave(" = 0");
276         return 0;
277 }
278
279 /*
280  * list the interface bound addresses to get the address and netmask
281  */
282 static int afs_rtm_getaddr(struct afs_rtm_desc *desc)
283 {
284         struct msghdr msg;
285         struct kvec iov[1];
286         int ret;
287
288         struct {
289                 struct nlmsghdr nl_msg __attribute__((aligned(NLMSG_ALIGNTO)));
290                 struct ifaddrmsg addr_msg __attribute__((aligned(NLMSG_ALIGNTO)));
291         } request;
292
293         _enter("");
294
295         memset(&request, 0, sizeof(request));
296
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;
302
303         memset(&msg, 0, sizeof(msg));
304         iov[0].iov_base = &request;
305         iov[0].iov_len = sizeof(request);
306
307         ret = kernel_sendmsg(desc->nlsock, &msg, iov, 1, iov[0].iov_len);
308         _leave(" = %d", ret);
309         return ret;
310 }
311
312 /*
313  * list the interface link statuses to get the MTUs
314  */
315 static int afs_rtm_getlink(struct afs_rtm_desc *desc)
316 {
317         struct msghdr msg;
318         struct kvec iov[1];
319         int ret;
320
321         struct {
322                 struct nlmsghdr nl_msg __attribute__((aligned(NLMSG_ALIGNTO)));
323                 struct ifinfomsg link_msg __attribute__((aligned(NLMSG_ALIGNTO)));
324         } request;
325
326         _enter("");
327
328         memset(&request, 0, sizeof(request));
329
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;
335
336         memset(&msg, 0, sizeof(msg));
337         iov[0].iov_base = &request;
338         iov[0].iov_len = sizeof(request);
339
340         ret = kernel_sendmsg(desc->nlsock, &msg, iov, 1, iov[0].iov_len);
341         _leave(" = %d", ret);
342         return ret;
343 }
344
345 /*
346  * cull any interface records for which there isn't an MTU value
347  */
348 static void afs_cull_interfaces(struct afs_rtm_desc *desc)
349 {
350         struct afs_interface *bufs = desc->bufs;
351         size_t nbufs = desc->nbufs;
352         int loop, point = 0;
353
354         _enter("{%zu}", nbufs);
355
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];
361                         }
362                         point++;
363                 }
364         }
365
366         desc->nbufs = point;
367         _leave(" [%zu/%zu]", desc->nbufs, nbufs);
368 }
369
370 /*
371  * get a list of this system's interface IPv4 addresses, netmasks and MTUs
372  * - returns the number of interface records in the buffer
373  */
374 int afs_get_ipv4_interfaces(struct afs_interface *bufs, size_t maxbufs,
375                             bool wantloopback)
376 {
377         struct afs_rtm_desc desc;
378         int ret, loop;
379
380         _enter("");
381
382         memset(&desc, 0, sizeof(desc));
383         desc.bufs = bufs;
384         desc.maxbufs = maxbufs;
385         desc.wantloopback = wantloopback;
386
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;
392         }
393
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;
402
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;
411
412         afs_cull_interfaces(&desc);
413         ret = desc.nbufs;
414
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);
421
422 error:
423         kfree(desc.data);
424         sock_release(desc.nlsock);
425         _leave(" = %d", ret);
426         return ret;
427 }
428
429 /*
430  * get a MAC address from a random ethernet interface that has a real one
431  * - the buffer should be 6 bytes in size
432  */
433 int afs_get_MAC_address(u8 mac[6])
434 {
435         struct afs_rtm_desc desc;
436         int ret;
437
438         _enter("");
439
440         memset(&desc, 0, sizeof(desc));
441         desc.mac = mac;
442         desc.mac_index = UINT_MAX;
443
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;
449         }
450
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;
459
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;
467         }
468
469 error:
470         sock_release(desc.nlsock);
471         _leave(" = %d", ret);
472         return ret;
473 }