aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorPierre Ynard <linkfanel@yahoo.fr>2007-10-11 00:22:05 -0400
committerDavid S. Miller <davem@davemloft.net>2007-10-11 00:22:05 -0400
commit31910575a9de61e78065e93846e8e7a4894a18bf (patch)
tree5dcc41957a62b8ec06575ebfe47c75828caf8bfb
parent092e9d93b3728d484a4e73df9852dc4002cf9923 (diff)
[IPv6]: Export userland ND options through netlink (RDNSS support)
As discussed before, this patch provides userland with a way to access relevant options in Router Advertisements, after they are processed and validated by the kernel. Extra options are processed in a generic way; this patch only exports RDNSS options described in RFC5006, but support to control which options are exported could be easily added. A new rtnetlink message type is defined, to transport Neighbor Discovery options, along with optional context information. At the moment only the address of the router sending an RDNSS option is included, but additional attributes may be later defined, if needed by new use cases. Signed-off-by: Pierre Ynard <linkfanel@yahoo.fr> Signed-off-by: David S. Miller <davem@davemloft.net>
-rw-r--r--include/linux/rtnetlink.h29
-rw-r--r--include/net/ndisc.h1
-rw-r--r--net/ipv6/ndisc.c103
3 files changed, 124 insertions, 9 deletions
diff --git a/include/linux/rtnetlink.h b/include/linux/rtnetlink.h
index dff3192374f8..5bf618241ab9 100644
--- a/include/linux/rtnetlink.h
+++ b/include/linux/rtnetlink.h
@@ -97,6 +97,9 @@ enum {
97 RTM_SETNEIGHTBL, 97 RTM_SETNEIGHTBL,
98#define RTM_SETNEIGHTBL RTM_SETNEIGHTBL 98#define RTM_SETNEIGHTBL RTM_SETNEIGHTBL
99 99
100 RTM_NEWNDUSEROPT = 68,
101#define RTM_NEWNDUSEROPT RTM_NEWNDUSEROPT
102
100 __RTM_MAX, 103 __RTM_MAX,
101#define RTM_MAX (((__RTM_MAX + 3) & ~3) - 1) 104#define RTM_MAX (((__RTM_MAX + 3) & ~3) - 1)
102}; 105};
@@ -479,6 +482,30 @@ enum
479#define TCA_RTA(r) ((struct rtattr*)(((char*)(r)) + NLMSG_ALIGN(sizeof(struct tcmsg)))) 482#define TCA_RTA(r) ((struct rtattr*)(((char*)(r)) + NLMSG_ALIGN(sizeof(struct tcmsg))))
480#define TCA_PAYLOAD(n) NLMSG_PAYLOAD(n,sizeof(struct tcmsg)) 483#define TCA_PAYLOAD(n) NLMSG_PAYLOAD(n,sizeof(struct tcmsg))
481 484
485/********************************************************************
486 * Neighbor Discovery userland options
487 ****/
488
489struct nduseroptmsg
490{
491 unsigned char nduseropt_family;
492 unsigned char nduseropt_pad1;
493 unsigned short nduseropt_opts_len; /* Total length of options */
494 __u8 nduseropt_icmp_type;
495 __u8 nduseropt_icmp_code;
496 unsigned short nduseropt_pad2;
497 /* Followed by one or more ND options */
498};
499
500enum
501{
502 NDUSEROPT_UNSPEC,
503 NDUSEROPT_SRCADDR,
504 __NDUSEROPT_MAX
505};
506
507#define NDUSEROPT_MAX (__NDUSEROPT_MAX - 1)
508
482#ifndef __KERNEL__ 509#ifndef __KERNEL__
483/* RTnetlink multicast groups - backwards compatibility for userspace */ 510/* RTnetlink multicast groups - backwards compatibility for userspace */
484#define RTMGRP_LINK 1 511#define RTMGRP_LINK 1
@@ -542,6 +569,8 @@ enum rtnetlink_groups {
542#define RTNLGRP_IPV6_PREFIX RTNLGRP_IPV6_PREFIX 569#define RTNLGRP_IPV6_PREFIX RTNLGRP_IPV6_PREFIX
543 RTNLGRP_IPV6_RULE, 570 RTNLGRP_IPV6_RULE,
544#define RTNLGRP_IPV6_RULE RTNLGRP_IPV6_RULE 571#define RTNLGRP_IPV6_RULE RTNLGRP_IPV6_RULE
572 RTNLGRP_ND_USEROPT,
573#define RTNLGRP_ND_USEROPT RTNLGRP_ND_USEROPT
545 __RTNLGRP_MAX 574 __RTNLGRP_MAX
546}; 575};
547#define RTNLGRP_MAX (__RTNLGRP_MAX - 1) 576#define RTNLGRP_MAX (__RTNLGRP_MAX - 1)
diff --git a/include/net/ndisc.h b/include/net/ndisc.h
index 475b10c575b3..6684f7efbeeb 100644
--- a/include/net/ndisc.h
+++ b/include/net/ndisc.h
@@ -24,6 +24,7 @@ enum {
24 ND_OPT_MTU = 5, /* RFC2461 */ 24 ND_OPT_MTU = 5, /* RFC2461 */
25 __ND_OPT_ARRAY_MAX, 25 __ND_OPT_ARRAY_MAX,
26 ND_OPT_ROUTE_INFO = 24, /* RFC4191 */ 26 ND_OPT_ROUTE_INFO = 24, /* RFC4191 */
27 ND_OPT_RDNSS = 25, /* RFC5006 */
27 __ND_OPT_MAX 28 __ND_OPT_MAX
28}; 29};
29 30
diff --git a/net/ipv6/ndisc.c b/net/ipv6/ndisc.c
index d4acd283111b..6cc33dc83d1c 100644
--- a/net/ipv6/ndisc.c
+++ b/net/ipv6/ndisc.c
@@ -15,9 +15,10 @@
15/* 15/*
16 * Changes: 16 * Changes:
17 * 17 *
18 * Pierre Ynard : export userland ND options
19 * through netlink (RDNSS support)
18 * Lars Fenneberg : fixed MTU setting on receipt 20 * Lars Fenneberg : fixed MTU setting on receipt
19 * of an RA. 21 * of an RA.
20 *
21 * Janos Farkas : kmalloc failure checks 22 * Janos Farkas : kmalloc failure checks
22 * Alexey Kuznetsov : state machine reworked 23 * Alexey Kuznetsov : state machine reworked
23 * and moved to net/core. 24 * and moved to net/core.
@@ -78,6 +79,9 @@
78#include <net/addrconf.h> 79#include <net/addrconf.h>
79#include <net/icmp.h> 80#include <net/icmp.h>
80 81
82#include <net/netlink.h>
83#include <linux/rtnetlink.h>
84
81#include <net/flow.h> 85#include <net/flow.h>
82#include <net/ip6_checksum.h> 86#include <net/ip6_checksum.h>
83#include <linux/proc_fs.h> 87#include <linux/proc_fs.h>
@@ -161,6 +165,8 @@ struct ndisc_options {
161 struct nd_opt_hdr *nd_opts_ri; 165 struct nd_opt_hdr *nd_opts_ri;
162 struct nd_opt_hdr *nd_opts_ri_end; 166 struct nd_opt_hdr *nd_opts_ri_end;
163#endif 167#endif
168 struct nd_opt_hdr *nd_useropts;
169 struct nd_opt_hdr *nd_useropts_end;
164}; 170};
165 171
166#define nd_opts_src_lladdr nd_opt_array[ND_OPT_SOURCE_LL_ADDR] 172#define nd_opts_src_lladdr nd_opt_array[ND_OPT_SOURCE_LL_ADDR]
@@ -225,6 +231,22 @@ static struct nd_opt_hdr *ndisc_next_option(struct nd_opt_hdr *cur,
225 return (cur <= end && cur->nd_opt_type == type ? cur : NULL); 231 return (cur <= end && cur->nd_opt_type == type ? cur : NULL);
226} 232}
227 233
234static inline int ndisc_is_useropt(struct nd_opt_hdr *opt)
235{
236 return (opt->nd_opt_type == ND_OPT_RDNSS);
237}
238
239static struct nd_opt_hdr *ndisc_next_useropt(struct nd_opt_hdr *cur,
240 struct nd_opt_hdr *end)
241{
242 if (!cur || !end || cur >= end)
243 return NULL;
244 do {
245 cur = ((void *)cur) + (cur->nd_opt_len << 3);
246 } while(cur < end && !ndisc_is_useropt(cur));
247 return (cur <= end && ndisc_is_useropt(cur) ? cur : NULL);
248}
249
228static struct ndisc_options *ndisc_parse_options(u8 *opt, int opt_len, 250static struct ndisc_options *ndisc_parse_options(u8 *opt, int opt_len,
229 struct ndisc_options *ndopts) 251 struct ndisc_options *ndopts)
230{ 252{
@@ -267,14 +289,21 @@ static struct ndisc_options *ndisc_parse_options(u8 *opt, int opt_len,
267 break; 289 break;
268#endif 290#endif
269 default: 291 default:
270 /* 292 if (ndisc_is_useropt(nd_opt)) {
271 * Unknown options must be silently ignored, 293 ndopts->nd_useropts_end = nd_opt;
272 * to accommodate future extension to the protocol. 294 if (!ndopts->nd_useropts)
273 */ 295 ndopts->nd_useropts = nd_opt;
274 ND_PRINTK2(KERN_NOTICE 296 } else {
275 "%s(): ignored unsupported option; type=%d, len=%d\n", 297 /*
276 __FUNCTION__, 298 * Unknown options must be silently ignored,
277 nd_opt->nd_opt_type, nd_opt->nd_opt_len); 299 * to accommodate future extension to the
300 * protocol.
301 */
302 ND_PRINTK2(KERN_NOTICE
303 "%s(): ignored unsupported option; type=%d, len=%d\n",
304 __FUNCTION__,
305 nd_opt->nd_opt_type, nd_opt->nd_opt_len);
306 }
278 } 307 }
279 opt_len -= l; 308 opt_len -= l;
280 nd_opt = ((void *)nd_opt) + l; 309 nd_opt = ((void *)nd_opt) + l;
@@ -984,6 +1013,53 @@ out:
984 in6_dev_put(idev); 1013 in6_dev_put(idev);
985} 1014}
986 1015
1016static void ndisc_ra_useropt(struct sk_buff *ra, struct nd_opt_hdr *opt)
1017{
1018 struct icmp6hdr *icmp6h = (struct icmp6hdr *)skb_transport_header(ra);
1019 struct sk_buff *skb;
1020 struct nlmsghdr *nlh;
1021 struct nduseroptmsg *ndmsg;
1022 int err;
1023 int base_size = NLMSG_ALIGN(sizeof(struct nduseroptmsg)
1024 + (opt->nd_opt_len << 3));
1025 size_t msg_size = base_size + nla_total_size(sizeof(struct in6_addr));
1026
1027 skb = nlmsg_new(msg_size, GFP_ATOMIC);
1028 if (skb == NULL) {
1029 err = -ENOBUFS;
1030 goto errout;
1031 }
1032
1033 nlh = nlmsg_put(skb, 0, 0, RTM_NEWNDUSEROPT, base_size, 0);
1034 if (nlh == NULL) {
1035 goto nla_put_failure;
1036 }
1037
1038 ndmsg = nlmsg_data(nlh);
1039 ndmsg->nduseropt_family = AF_INET6;
1040 ndmsg->nduseropt_icmp_type = icmp6h->icmp6_type;
1041 ndmsg->nduseropt_icmp_code = icmp6h->icmp6_code;
1042 ndmsg->nduseropt_opts_len = opt->nd_opt_len << 3;
1043
1044 memcpy(ndmsg + 1, opt, opt->nd_opt_len << 3);
1045
1046 NLA_PUT(skb, NDUSEROPT_SRCADDR, sizeof(struct in6_addr),
1047 &ipv6_hdr(ra)->saddr);
1048 nlmsg_end(skb, nlh);
1049
1050 err = rtnl_notify(skb, 0, RTNLGRP_ND_USEROPT, NULL, GFP_ATOMIC);
1051 if (err < 0)
1052 goto errout;
1053
1054 return;
1055
1056nla_put_failure:
1057 nlmsg_free(skb);
1058 err = -EMSGSIZE;
1059errout:
1060 rtnl_set_sk_err(RTNLGRP_ND_USEROPT, err);
1061}
1062
987static void ndisc_router_discovery(struct sk_buff *skb) 1063static void ndisc_router_discovery(struct sk_buff *skb)
988{ 1064{
989 struct ra_msg *ra_msg = (struct ra_msg *)skb_transport_header(skb); 1065 struct ra_msg *ra_msg = (struct ra_msg *)skb_transport_header(skb);
@@ -1216,6 +1292,15 @@ skip_defrtr:
1216 } 1292 }
1217 } 1293 }
1218 1294
1295 if (ndopts.nd_useropts) {
1296 struct nd_opt_hdr *opt;
1297 for (opt = ndopts.nd_useropts;
1298 opt;
1299 opt = ndisc_next_useropt(opt, ndopts.nd_useropts_end)) {
1300 ndisc_ra_useropt(skb, opt);
1301 }
1302 }
1303
1219 if (ndopts.nd_opts_tgt_lladdr || ndopts.nd_opts_rh) { 1304 if (ndopts.nd_opts_tgt_lladdr || ndopts.nd_opts_rh) {
1220 ND_PRINTK2(KERN_WARNING 1305 ND_PRINTK2(KERN_WARNING
1221 "ICMPv6 RA: invalid RA options"); 1306 "ICMPv6 RA: invalid RA options");