aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-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");