aboutsummaryrefslogtreecommitdiffstats
path: root/net/ipv6/ndisc.c
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 /net/ipv6/ndisc.c
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>
Diffstat (limited to 'net/ipv6/ndisc.c')
-rw-r--r--net/ipv6/ndisc.c103
1 files changed, 94 insertions, 9 deletions
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");