diff options
-rw-r--r-- | fs/afs/Makefile | 1 | ||||
-rw-r--r-- | fs/afs/afs_cm.h | 3 | ||||
-rw-r--r-- | fs/afs/cmservice.c | 98 | ||||
-rw-r--r-- | fs/afs/internal.h | 42 | ||||
-rw-r--r-- | fs/afs/main.c | 49 | ||||
-rw-r--r-- | fs/afs/rxrpc.c | 39 | ||||
-rw-r--r-- | fs/afs/use-rtnetlink.c | 473 |
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); |
23 | static int afs_deliver_cb_probe(struct afs_call *, struct sk_buff *, bool); | 23 | static int afs_deliver_cb_probe(struct afs_call *, struct sk_buff *, bool); |
24 | static int afs_deliver_cb_callback(struct afs_call *, struct sk_buff *, bool); | 24 | static int afs_deliver_cb_callback(struct afs_call *, struct sk_buff *, bool); |
25 | static int afs_deliver_cb_get_capabilities(struct afs_call *, struct sk_buff *, | ||
26 | bool); | ||
25 | static void afs_cm_destructor(struct afs_call *); | 27 | static 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 | */ | ||
62 | static 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 | */ | ||
350 | static 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 | */ | ||
412 | static 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 | */ | ||
352 | struct 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 | */ | ||
367 | struct 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 | */ |
467 | extern struct afs_uuid afs_uuid; | ||
433 | #ifdef AFS_CACHING_SUPPORT | 468 | #ifdef AFS_CACHING_SUPPORT |
434 | extern struct cachefs_netfs afs_cache_netfs; | 469 | extern 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 *, | |||
470 | extern void afs_flat_call_destructor(struct afs_call *); | 505 | extern void afs_flat_call_destructor(struct afs_call *); |
471 | extern void afs_transfer_reply(struct afs_call *, struct sk_buff *); | 506 | extern void afs_transfer_reply(struct afs_call *, struct sk_buff *); |
472 | extern void afs_send_empty_reply(struct afs_call *); | 507 | extern void afs_send_empty_reply(struct afs_call *); |
508 | extern void afs_send_simple_reply(struct afs_call *, const void *, size_t); | ||
473 | extern int afs_extract_data(struct afs_call *, struct sk_buff *, bool, void *, | 509 | extern 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); | |||
501 | extern void afs_fs_exit(void); | 537 | extern void afs_fs_exit(void); |
502 | 538 | ||
503 | /* | 539 | /* |
540 | * use-rtnetlink.c | ||
541 | */ | ||
542 | extern int afs_get_ipv4_interfaces(struct afs_interface *, size_t, bool); | ||
543 | extern 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 | ||
43 | struct afs_uuid afs_uuid; | ||
44 | |||
45 | /* | ||
46 | * get a client UUID | ||
47 | */ | ||
48 | static 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 | */ | ||
719 | void 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 | */ |
719 | int afs_extract_data(struct afs_call *call, struct sk_buff *skb, | 758 | int 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 | |||
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 | } | ||