aboutsummaryrefslogtreecommitdiffstats
path: root/net/ipv6/ndisc.c
diff options
context:
space:
mode:
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");