diff options
-rw-r--r-- | include/linux/rtnetlink.h | 29 | ||||
-rw-r--r-- | include/net/ndisc.h | 1 | ||||
-rw-r--r-- | net/ipv6/ndisc.c | 103 |
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 | |||
489 | struct 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 | |||
500 | enum | ||
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 | ||
234 | static inline int ndisc_is_useropt(struct nd_opt_hdr *opt) | ||
235 | { | ||
236 | return (opt->nd_opt_type == ND_OPT_RDNSS); | ||
237 | } | ||
238 | |||
239 | static 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 | |||
228 | static struct ndisc_options *ndisc_parse_options(u8 *opt, int opt_len, | 250 | static 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 | ||
1016 | static 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 | |||
1056 | nla_put_failure: | ||
1057 | nlmsg_free(skb); | ||
1058 | err = -EMSGSIZE; | ||
1059 | errout: | ||
1060 | rtnl_set_sk_err(RTNLGRP_ND_USEROPT, err); | ||
1061 | } | ||
1062 | |||
987 | static void ndisc_router_discovery(struct sk_buff *skb) | 1063 | static 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"); |