aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDavid Howells <dhowells@redhat.com>2007-04-26 18:58:17 -0400
committerDavid S. Miller <davem@davemloft.net>2007-04-26 18:58:17 -0400
commitb908fe6b2d1294d93b0d0badf6bf4f9a2cd7d729 (patch)
treea0ccc4a21752f69991f762faea7c1b40510dbb4c
parent0795e7c031c4bda46fbdde678adf29de19bef7f4 (diff)
[AFS]: Add support for the CB.GetCapabilities operation.
Add support for the CB.GetCapabilities operation with which the fileserver can ask the client for the following information: (1) The list of network interfaces it has available as IPv4 address + netmask plus the MTUs. (2) The client's UUID. (3) The extended capabilities of the client, for which the only current one is unified error mapping (abort code interpretation). To support this, the patch adds the following routines to AFS: (1) A function to iterate through all the network interfaces using RTNETLINK to extract IPv4 addresses and MTUs. (2) A function to iterate through all the network interfaces using RTNETLINK to pull out the MAC address of the lowest index interface to use in UUID construction. Signed-off-by: David Howells <dhowells@redhat.com> Signed-off-by: David S. Miller <davem@davemloft.net>
-rw-r--r--fs/afs/Makefile1
-rw-r--r--fs/afs/afs_cm.h3
-rw-r--r--fs/afs/cmservice.c98
-rw-r--r--fs/afs/internal.h42
-rw-r--r--fs/afs/main.c49
-rw-r--r--fs/afs/rxrpc.c39
-rw-r--r--fs/afs/use-rtnetlink.c473
7 files changed, 705 insertions, 0 deletions
diff --git a/fs/afs/Makefile b/fs/afs/Makefile
index cca198b2caed..01545eb1d872 100644
--- a/fs/afs/Makefile
+++ b/fs/afs/Makefile
@@ -18,6 +18,7 @@ kafs-objs := \
18 security.o \ 18 security.o \
19 server.o \ 19 server.o \
20 super.o \ 20 super.o \
21 use-rtnetlink.o \
21 vlclient.o \ 22 vlclient.o \
22 vlocation.o \ 23 vlocation.o \
23 vnode.o \ 24 vnode.o \
diff --git a/fs/afs/afs_cm.h b/fs/afs/afs_cm.h
index 7c8e3d43c8e5..d4bd201cc31e 100644
--- a/fs/afs/afs_cm.h
+++ b/fs/afs/afs_cm.h
@@ -23,6 +23,9 @@ enum AFS_CM_Operations {
23 CBGetCE = 208, /* get cache file description */ 23 CBGetCE = 208, /* get cache file description */
24 CBGetXStatsVersion = 209, /* get version of extended statistics */ 24 CBGetXStatsVersion = 209, /* get version of extended statistics */
25 CBGetXStats = 210, /* get contents of extended statistics data */ 25 CBGetXStats = 210, /* get contents of extended statistics data */
26 CBGetCapabilities = 65538, /* get client capabilities */
26}; 27};
27 28
29#define AFS_CAP_ERROR_TRANSLATION 0x1
30
28#endif /* AFS_FS_H */ 31#endif /* AFS_FS_H */
diff --git a/fs/afs/cmservice.c b/fs/afs/cmservice.c
index c3ec57a237bf..a6af3acf016e 100644
--- a/fs/afs/cmservice.c
+++ b/fs/afs/cmservice.c
@@ -22,6 +22,8 @@ static int afs_deliver_cb_init_call_back_state(struct afs_call *,
22 struct sk_buff *, bool); 22 struct sk_buff *, bool);
23static int afs_deliver_cb_probe(struct afs_call *, struct sk_buff *, bool); 23static int afs_deliver_cb_probe(struct afs_call *, struct sk_buff *, bool);
24static int afs_deliver_cb_callback(struct afs_call *, struct sk_buff *, bool); 24static int afs_deliver_cb_callback(struct afs_call *, struct sk_buff *, bool);
25static int afs_deliver_cb_get_capabilities(struct afs_call *, struct sk_buff *,
26 bool);
25static void afs_cm_destructor(struct afs_call *); 27static void afs_cm_destructor(struct afs_call *);
26 28
27/* 29/*
@@ -55,6 +57,16 @@ static const struct afs_call_type afs_SRXCBProbe = {
55}; 57};
56 58
57/* 59/*
60 * CB.GetCapabilities operation type
61 */
62static const struct afs_call_type afs_SRXCBGetCapabilites = {
63 .name = "CB.GetCapabilities",
64 .deliver = afs_deliver_cb_get_capabilities,
65 .abort_to_error = afs_abort_to_error,
66 .destructor = afs_cm_destructor,
67};
68
69/*
58 * route an incoming cache manager call 70 * route an incoming cache manager call
59 * - return T if supported, F if not 71 * - return T if supported, F if not
60 */ 72 */
@@ -74,6 +86,9 @@ bool afs_cm_incoming_call(struct afs_call *call)
74 case CBProbe: 86 case CBProbe:
75 call->type = &afs_SRXCBProbe; 87 call->type = &afs_SRXCBProbe;
76 return true; 88 return true;
89 case CBGetCapabilities:
90 call->type = &afs_SRXCBGetCapabilites;
91 return true;
77 default: 92 default:
78 return false; 93 return false;
79 } 94 }
@@ -328,3 +343,86 @@ static int afs_deliver_cb_probe(struct afs_call *call, struct sk_buff *skb,
328 schedule_work(&call->work); 343 schedule_work(&call->work);
329 return 0; 344 return 0;
330} 345}
346
347/*
348 * allow the fileserver to ask about the cache manager's capabilities
349 */
350static void SRXAFSCB_GetCapabilities(struct work_struct *work)
351{
352 struct afs_interface *ifs;
353 struct afs_call *call = container_of(work, struct afs_call, work);
354 int loop, nifs;
355
356 struct {
357 struct /* InterfaceAddr */ {
358 __be32 nifs;
359 __be32 uuid[11];
360 __be32 ifaddr[32];
361 __be32 netmask[32];
362 __be32 mtu[32];
363 } ia;
364 struct /* Capabilities */ {
365 __be32 capcount;
366 __be32 caps[1];
367 } cap;
368 } reply;
369
370 _enter("");
371
372 nifs = 0;
373 ifs = kcalloc(32, sizeof(*ifs), GFP_KERNEL);
374 if (ifs) {
375 nifs = afs_get_ipv4_interfaces(ifs, 32, false);
376 if (nifs < 0) {
377 kfree(ifs);
378 ifs = NULL;
379 nifs = 0;
380 }
381 }
382
383 memset(&reply, 0, sizeof(reply));
384 reply.ia.nifs = htonl(nifs);
385
386 reply.ia.uuid[0] = htonl(afs_uuid.time_low);
387 reply.ia.uuid[1] = htonl(afs_uuid.time_mid);
388 reply.ia.uuid[2] = htonl(afs_uuid.time_hi_and_version);
389 reply.ia.uuid[3] = htonl((s8) afs_uuid.clock_seq_hi_and_reserved);
390 reply.ia.uuid[4] = htonl((s8) afs_uuid.clock_seq_low);
391 for (loop = 0; loop < 6; loop++)
392 reply.ia.uuid[loop + 5] = htonl((s8) afs_uuid.node[loop]);
393
394 if (ifs) {
395 for (loop = 0; loop < nifs; loop++) {
396 reply.ia.ifaddr[loop] = ifs[loop].address.s_addr;
397 reply.ia.netmask[loop] = ifs[loop].netmask.s_addr;
398 reply.ia.mtu[loop] = htonl(ifs[loop].mtu);
399 }
400 }
401
402 reply.cap.capcount = htonl(1);
403 reply.cap.caps[0] = htonl(AFS_CAP_ERROR_TRANSLATION);
404 afs_send_simple_reply(call, &reply, sizeof(reply));
405
406 _leave("");
407}
408
409/*
410 * deliver request data to a CB.GetCapabilities call
411 */
412static int afs_deliver_cb_get_capabilities(struct afs_call *call,
413 struct sk_buff *skb, bool last)
414{
415 _enter(",{%u},%d", skb->len, last);
416
417 if (skb->len > 0)
418 return -EBADMSG;
419 if (!last)
420 return 0;
421
422 /* no unmarshalling required */
423 call->state = AFS_CALL_REPLYING;
424
425 INIT_WORK(&call->work, SRXAFSCB_GetCapabilities);
426 schedule_work(&call->work);
427 return 0;
428}
diff --git a/fs/afs/internal.h b/fs/afs/internal.h
index 8bed2429d01f..6120d4bd19e0 100644
--- a/fs/afs/internal.h
+++ b/fs/afs/internal.h
@@ -346,6 +346,40 @@ struct afs_permits {
346 struct afs_permit permits[0]; /* the permits so far examined */ 346 struct afs_permit permits[0]; /* the permits so far examined */
347}; 347};
348 348
349/*
350 * record of one of a system's set of network interfaces
351 */
352struct afs_interface {
353 unsigned index; /* interface index */
354 struct in_addr address; /* IPv4 address bound to interface */
355 struct in_addr netmask; /* netmask applied to address */
356 unsigned mtu; /* MTU of interface */
357};
358
359/*
360 * UUID definition [internet draft]
361 * - the timestamp is a 60-bit value, split 32/16/12, and goes in 100ns
362 * increments since midnight 15th October 1582
363 * - add AFS_UUID_TO_UNIX_TIME to convert unix time in 100ns units to UUID
364 * time
365 * - the clock sequence is a 14-bit counter to avoid duplicate times
366 */
367struct afs_uuid {
368 u32 time_low; /* low part of timestamp */
369 u16 time_mid; /* mid part of timestamp */
370 u16 time_hi_and_version; /* high part of timestamp and version */
371#define AFS_UUID_TO_UNIX_TIME 0x01b21dd213814000
372#define AFS_UUID_TIMEHI_MASK 0x0fff
373#define AFS_UUID_VERSION_TIME 0x1000 /* time-based UUID */
374#define AFS_UUID_VERSION_NAME 0x3000 /* name-based UUID */
375#define AFS_UUID_VERSION_RANDOM 0x4000 /* (pseudo-)random generated UUID */
376 u8 clock_seq_hi_and_reserved; /* clock seq hi and variant */
377#define AFS_UUID_CLOCKHI_MASK 0x3f
378#define AFS_UUID_VARIANT_STD 0x80
379 u8 clock_seq_low; /* clock seq low */
380 u8 node[6]; /* spatially unique node ID (MAC addr) */
381};
382
349/*****************************************************************************/ 383/*****************************************************************************/
350/* 384/*
351 * callback.c 385 * callback.c
@@ -430,6 +464,7 @@ extern void afs_clear_inode(struct inode *);
430/* 464/*
431 * main.c 465 * main.c
432 */ 466 */
467extern struct afs_uuid afs_uuid;
433#ifdef AFS_CACHING_SUPPORT 468#ifdef AFS_CACHING_SUPPORT
434extern struct cachefs_netfs afs_cache_netfs; 469extern struct cachefs_netfs afs_cache_netfs;
435#endif 470#endif
@@ -470,6 +505,7 @@ extern struct afs_call *afs_alloc_flat_call(const struct afs_call_type *,
470extern void afs_flat_call_destructor(struct afs_call *); 505extern void afs_flat_call_destructor(struct afs_call *);
471extern void afs_transfer_reply(struct afs_call *, struct sk_buff *); 506extern void afs_transfer_reply(struct afs_call *, struct sk_buff *);
472extern void afs_send_empty_reply(struct afs_call *); 507extern void afs_send_empty_reply(struct afs_call *);
508extern void afs_send_simple_reply(struct afs_call *, const void *, size_t);
473extern int afs_extract_data(struct afs_call *, struct sk_buff *, bool, void *, 509extern int afs_extract_data(struct afs_call *, struct sk_buff *, bool, void *,
474 size_t); 510 size_t);
475 511
@@ -501,6 +537,12 @@ extern int afs_fs_init(void);
501extern void afs_fs_exit(void); 537extern void afs_fs_exit(void);
502 538
503/* 539/*
540 * use-rtnetlink.c
541 */
542extern int afs_get_ipv4_interfaces(struct afs_interface *, size_t, bool);
543extern int afs_get_MAC_address(u8 [6]);
544
545/*
504 * vlclient.c 546 * vlclient.c
505 */ 547 */
506#ifdef AFS_CACHING_SUPPORT 548#ifdef AFS_CACHING_SUPPORT
diff --git a/fs/afs/main.c b/fs/afs/main.c
index 0cf1b021ad54..40c2704e7557 100644
--- a/fs/afs/main.c
+++ b/fs/afs/main.c
@@ -40,6 +40,51 @@ struct cachefs_netfs afs_cache_netfs = {
40}; 40};
41#endif 41#endif
42 42
43struct afs_uuid afs_uuid;
44
45/*
46 * get a client UUID
47 */
48static int __init afs_get_client_UUID(void)
49{
50 struct timespec ts;
51 u64 uuidtime;
52 u16 clockseq;
53 int ret;
54
55 /* read the MAC address of one of the external interfaces and construct
56 * a UUID from it */
57 ret = afs_get_MAC_address(afs_uuid.node);
58 if (ret < 0)
59 return ret;
60
61 getnstimeofday(&ts);
62 uuidtime = (u64) ts.tv_sec * 1000 * 1000 * 10;
63 uuidtime += ts.tv_nsec / 100;
64 uuidtime += AFS_UUID_TO_UNIX_TIME;
65 afs_uuid.time_low = uuidtime;
66 afs_uuid.time_mid = uuidtime >> 32;
67 afs_uuid.time_hi_and_version = (uuidtime >> 48) & AFS_UUID_TIMEHI_MASK;
68 afs_uuid.time_hi_and_version = AFS_UUID_VERSION_TIME;
69
70 get_random_bytes(&clockseq, 2);
71 afs_uuid.clock_seq_low = clockseq;
72 afs_uuid.clock_seq_hi_and_reserved =
73 (clockseq >> 8) & AFS_UUID_CLOCKHI_MASK;
74 afs_uuid.clock_seq_hi_and_reserved = AFS_UUID_VARIANT_STD;
75
76 _debug("AFS UUID: %08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x",
77 afs_uuid.time_low,
78 afs_uuid.time_mid,
79 afs_uuid.time_hi_and_version,
80 afs_uuid.clock_seq_hi_and_reserved,
81 afs_uuid.clock_seq_low,
82 afs_uuid.node[0], afs_uuid.node[1], afs_uuid.node[2],
83 afs_uuid.node[3], afs_uuid.node[4], afs_uuid.node[5]);
84
85 return 0;
86}
87
43/* 88/*
44 * initialise the AFS client FS module 89 * initialise the AFS client FS module
45 */ 90 */
@@ -49,6 +94,10 @@ static int __init afs_init(void)
49 94
50 printk(KERN_INFO "kAFS: Red Hat AFS client v0.1 registering.\n"); 95 printk(KERN_INFO "kAFS: Red Hat AFS client v0.1 registering.\n");
51 96
97 ret = afs_get_client_UUID();
98 if (ret < 0)
99 return ret;
100
52 /* register the /proc stuff */ 101 /* register the /proc stuff */
53 ret = afs_proc_init(); 102 ret = afs_proc_init();
54 if (ret < 0) 103 if (ret < 0)
diff --git a/fs/afs/rxrpc.c b/fs/afs/rxrpc.c
index e86c527d87a1..e7b047328a39 100644
--- a/fs/afs/rxrpc.c
+++ b/fs/afs/rxrpc.c
@@ -714,6 +714,45 @@ void afs_send_empty_reply(struct afs_call *call)
714} 714}
715 715
716/* 716/*
717 * send a simple reply
718 */
719void afs_send_simple_reply(struct afs_call *call, const void *buf, size_t len)
720{
721 struct msghdr msg;
722 struct iovec iov[1];
723
724 _enter("");
725
726 iov[0].iov_base = (void *) buf;
727 iov[0].iov_len = len;
728 msg.msg_name = NULL;
729 msg.msg_namelen = 0;
730 msg.msg_iov = iov;
731 msg.msg_iovlen = 1;
732 msg.msg_control = NULL;
733 msg.msg_controllen = 0;
734 msg.msg_flags = 0;
735
736 call->state = AFS_CALL_AWAIT_ACK;
737 switch (rxrpc_kernel_send_data(call->rxcall, &msg, len)) {
738 case 0:
739 _leave(" [replied]");
740 return;
741
742 case -ENOMEM:
743 _debug("oom");
744 rxrpc_kernel_abort_call(call->rxcall, RX_USER_ABORT);
745 default:
746 rxrpc_kernel_end_call(call->rxcall);
747 call->rxcall = NULL;
748 call->type->destructor(call);
749 afs_free_call(call);
750 _leave(" [error]");
751 return;
752 }
753}
754
755/*
717 * extract a piece of data from the received data socket buffers 756 * extract a piece of data from the received data socket buffers
718 */ 757 */
719int afs_extract_data(struct afs_call *call, struct sk_buff *skb, 758int afs_extract_data(struct afs_call *call, struct sk_buff *skb,
diff --git a/fs/afs/use-rtnetlink.c b/fs/afs/use-rtnetlink.c
new file mode 100644
index 000000000000..82f0daa28970
--- /dev/null
+++ b/fs/afs/use-rtnetlink.c
@@ -0,0 +1,473 @@
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
19struct 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 */
37static 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 */
91static 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
113found:
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 */
146static 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 */
201static 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 */
282static 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 */
315static 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 */
348static 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 */
374int 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
422error:
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 */
433int 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
469error:
470 sock_release(desc.nlsock);
471 _leave(" = %d", ret);
472 return ret;
473}