Commit cf06eef0 authored by David S. Miller's avatar David S. Miller

Merge branch 'icmp6-drop-reason'

Eric Dumazet says:

====================
ipv6: icmp6: better drop reason support

This series aims to have more precise drop reason reports for icmp6.

This should reduce false positives on most usual cases.

This can be extended as needed later.
====================
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents dd1b5278 ac03694b
...@@ -76,6 +76,8 @@ ...@@ -76,6 +76,8 @@
FN(IPV6_NDISC_FRAG) \ FN(IPV6_NDISC_FRAG) \
FN(IPV6_NDISC_HOP_LIMIT) \ FN(IPV6_NDISC_HOP_LIMIT) \
FN(IPV6_NDISC_BAD_CODE) \ FN(IPV6_NDISC_BAD_CODE) \
FN(IPV6_NDISC_BAD_OPTIONS) \
FN(IPV6_NDISC_NS_OTHERHOST) \
FNe(MAX) FNe(MAX)
/** /**
...@@ -330,6 +332,12 @@ enum skb_drop_reason { ...@@ -330,6 +332,12 @@ enum skb_drop_reason {
SKB_DROP_REASON_IPV6_NDISC_HOP_LIMIT, SKB_DROP_REASON_IPV6_NDISC_HOP_LIMIT,
/** @SKB_DROP_REASON_IPV6_NDISC_BAD_CODE: invalid NDISC icmp6 code. */ /** @SKB_DROP_REASON_IPV6_NDISC_BAD_CODE: invalid NDISC icmp6 code. */
SKB_DROP_REASON_IPV6_NDISC_BAD_CODE, SKB_DROP_REASON_IPV6_NDISC_BAD_CODE,
/** @SKB_DROP_REASON_IPV6_NDISC_BAD_OPTIONS: invalid NDISC options. */
SKB_DROP_REASON_IPV6_NDISC_BAD_OPTIONS,
/** @SKB_DROP_REASON_IPV6_NDISC_NS_OTHERHOST: NEIGHBOUR SOLICITATION
* for another host.
*/
SKB_DROP_REASON_IPV6_NDISC_NS_OTHERHOST,
/** /**
* @SKB_DROP_REASON_MAX: the maximum of drop reason, which shouldn't be * @SKB_DROP_REASON_MAX: the maximum of drop reason, which shouldn't be
* used as a real 'reason' * used as a real 'reason'
......
...@@ -705,7 +705,7 @@ int ip6_err_gen_icmpv6_unreach(struct sk_buff *skb, int nhs, int type, ...@@ -705,7 +705,7 @@ int ip6_err_gen_icmpv6_unreach(struct sk_buff *skb, int nhs, int type,
} }
EXPORT_SYMBOL(ip6_err_gen_icmpv6_unreach); EXPORT_SYMBOL(ip6_err_gen_icmpv6_unreach);
static void icmpv6_echo_reply(struct sk_buff *skb) static enum skb_drop_reason icmpv6_echo_reply(struct sk_buff *skb)
{ {
struct net *net = dev_net(skb->dev); struct net *net = dev_net(skb->dev);
struct sock *sk; struct sock *sk;
...@@ -719,18 +719,19 @@ static void icmpv6_echo_reply(struct sk_buff *skb) ...@@ -719,18 +719,19 @@ static void icmpv6_echo_reply(struct sk_buff *skb)
struct dst_entry *dst; struct dst_entry *dst;
struct ipcm6_cookie ipc6; struct ipcm6_cookie ipc6;
u32 mark = IP6_REPLY_MARK(net, skb->mark); u32 mark = IP6_REPLY_MARK(net, skb->mark);
SKB_DR(reason);
bool acast; bool acast;
u8 type; u8 type;
if (ipv6_addr_is_multicast(&ipv6_hdr(skb)->daddr) && if (ipv6_addr_is_multicast(&ipv6_hdr(skb)->daddr) &&
net->ipv6.sysctl.icmpv6_echo_ignore_multicast) net->ipv6.sysctl.icmpv6_echo_ignore_multicast)
return; return reason;
saddr = &ipv6_hdr(skb)->daddr; saddr = &ipv6_hdr(skb)->daddr;
acast = ipv6_anycast_destination(skb_dst(skb), saddr); acast = ipv6_anycast_destination(skb_dst(skb), saddr);
if (acast && net->ipv6.sysctl.icmpv6_echo_ignore_anycast) if (acast && net->ipv6.sysctl.icmpv6_echo_ignore_anycast)
return; return reason;
if (!ipv6_unicast_destination(skb) && if (!ipv6_unicast_destination(skb) &&
!(net->ipv6.sysctl.anycast_src_echo_reply && acast)) !(net->ipv6.sysctl.anycast_src_echo_reply && acast))
...@@ -804,6 +805,7 @@ static void icmpv6_echo_reply(struct sk_buff *skb) ...@@ -804,6 +805,7 @@ static void icmpv6_echo_reply(struct sk_buff *skb)
} else { } else {
icmpv6_push_pending_frames(sk, &fl6, &tmp_hdr, icmpv6_push_pending_frames(sk, &fl6, &tmp_hdr,
skb->len + sizeof(struct icmp6hdr)); skb->len + sizeof(struct icmp6hdr));
reason = SKB_CONSUMED;
} }
out_dst_release: out_dst_release:
dst_release(dst); dst_release(dst);
...@@ -811,6 +813,7 @@ static void icmpv6_echo_reply(struct sk_buff *skb) ...@@ -811,6 +813,7 @@ static void icmpv6_echo_reply(struct sk_buff *skb)
icmpv6_xmit_unlock(sk); icmpv6_xmit_unlock(sk);
out_bh_enable: out_bh_enable:
local_bh_enable(); local_bh_enable();
return reason;
} }
enum skb_drop_reason icmpv6_notify(struct sk_buff *skb, u8 type, enum skb_drop_reason icmpv6_notify(struct sk_buff *skb, u8 type,
...@@ -929,12 +932,12 @@ static int icmpv6_rcv(struct sk_buff *skb) ...@@ -929,12 +932,12 @@ static int icmpv6_rcv(struct sk_buff *skb)
switch (type) { switch (type) {
case ICMPV6_ECHO_REQUEST: case ICMPV6_ECHO_REQUEST:
if (!net->ipv6.sysctl.icmpv6_echo_ignore_all) if (!net->ipv6.sysctl.icmpv6_echo_ignore_all)
icmpv6_echo_reply(skb); reason = icmpv6_echo_reply(skb);
break; break;
case ICMPV6_EXT_ECHO_REQUEST: case ICMPV6_EXT_ECHO_REQUEST:
if (!net->ipv6.sysctl.icmpv6_echo_ignore_all && if (!net->ipv6.sysctl.icmpv6_echo_ignore_all &&
READ_ONCE(net->ipv4.sysctl_icmp_echo_enable_probe)) READ_ONCE(net->ipv4.sysctl_icmp_echo_enable_probe))
icmpv6_echo_reply(skb); reason = icmpv6_echo_reply(skb);
break; break;
case ICMPV6_ECHO_REPLY: case ICMPV6_ECHO_REPLY:
......
...@@ -783,7 +783,7 @@ void ndisc_update(const struct net_device *dev, struct neighbour *neigh, ...@@ -783,7 +783,7 @@ void ndisc_update(const struct net_device *dev, struct neighbour *neigh,
ndisc_ops_update(dev, neigh, flags, icmp6_type, ndopts); ndisc_ops_update(dev, neigh, flags, icmp6_type, ndopts);
} }
static void ndisc_recv_ns(struct sk_buff *skb) static enum skb_drop_reason ndisc_recv_ns(struct sk_buff *skb)
{ {
struct nd_msg *msg = (struct nd_msg *)skb_transport_header(skb); struct nd_msg *msg = (struct nd_msg *)skb_transport_header(skb);
const struct in6_addr *saddr = &ipv6_hdr(skb)->saddr; const struct in6_addr *saddr = &ipv6_hdr(skb)->saddr;
...@@ -797,18 +797,17 @@ static void ndisc_recv_ns(struct sk_buff *skb) ...@@ -797,18 +797,17 @@ static void ndisc_recv_ns(struct sk_buff *skb)
struct inet6_dev *idev = NULL; struct inet6_dev *idev = NULL;
struct neighbour *neigh; struct neighbour *neigh;
int dad = ipv6_addr_any(saddr); int dad = ipv6_addr_any(saddr);
bool inc;
int is_router = -1; int is_router = -1;
SKB_DR(reason);
u64 nonce = 0; u64 nonce = 0;
bool inc;
if (skb->len < sizeof(struct nd_msg)) { if (skb->len < sizeof(struct nd_msg))
ND_PRINTK(2, warn, "NS: packet too short\n"); return SKB_DROP_REASON_PKT_TOO_SMALL;
return;
}
if (ipv6_addr_is_multicast(&msg->target)) { if (ipv6_addr_is_multicast(&msg->target)) {
ND_PRINTK(2, warn, "NS: multicast target address\n"); ND_PRINTK(2, warn, "NS: multicast target address\n");
return; return reason;
} }
/* /*
...@@ -817,20 +816,18 @@ static void ndisc_recv_ns(struct sk_buff *skb) ...@@ -817,20 +816,18 @@ static void ndisc_recv_ns(struct sk_buff *skb)
*/ */
if (dad && !ipv6_addr_is_solict_mult(daddr)) { if (dad && !ipv6_addr_is_solict_mult(daddr)) {
ND_PRINTK(2, warn, "NS: bad DAD packet (wrong destination)\n"); ND_PRINTK(2, warn, "NS: bad DAD packet (wrong destination)\n");
return; return reason;
} }
if (!ndisc_parse_options(dev, msg->opt, ndoptlen, &ndopts)) { if (!ndisc_parse_options(dev, msg->opt, ndoptlen, &ndopts))
ND_PRINTK(2, warn, "NS: invalid ND options\n"); return SKB_DROP_REASON_IPV6_NDISC_BAD_OPTIONS;
return;
}
if (ndopts.nd_opts_src_lladdr) { if (ndopts.nd_opts_src_lladdr) {
lladdr = ndisc_opt_addr_data(ndopts.nd_opts_src_lladdr, dev); lladdr = ndisc_opt_addr_data(ndopts.nd_opts_src_lladdr, dev);
if (!lladdr) { if (!lladdr) {
ND_PRINTK(2, warn, ND_PRINTK(2, warn,
"NS: invalid link-layer address length\n"); "NS: invalid link-layer address length\n");
return; return reason;
} }
/* RFC2461 7.1.1: /* RFC2461 7.1.1:
...@@ -841,7 +838,7 @@ static void ndisc_recv_ns(struct sk_buff *skb) ...@@ -841,7 +838,7 @@ static void ndisc_recv_ns(struct sk_buff *skb)
if (dad) { if (dad) {
ND_PRINTK(2, warn, ND_PRINTK(2, warn,
"NS: bad DAD packet (link-layer address option)\n"); "NS: bad DAD packet (link-layer address option)\n");
return; return reason;
} }
} }
if (ndopts.nd_opts_nonce && ndopts.nd_opts_nonce->nd_opt_len == 1) if (ndopts.nd_opts_nonce && ndopts.nd_opts_nonce->nd_opt_len == 1)
...@@ -869,7 +866,7 @@ static void ndisc_recv_ns(struct sk_buff *skb) ...@@ -869,7 +866,7 @@ static void ndisc_recv_ns(struct sk_buff *skb)
* so fail our DAD process * so fail our DAD process
*/ */
addrconf_dad_failure(skb, ifp); addrconf_dad_failure(skb, ifp);
return; return reason;
} else { } else {
/* /*
* This is not a dad solicitation. * This is not a dad solicitation.
...@@ -901,7 +898,7 @@ static void ndisc_recv_ns(struct sk_buff *skb) ...@@ -901,7 +898,7 @@ static void ndisc_recv_ns(struct sk_buff *skb)
idev = in6_dev_get(dev); idev = in6_dev_get(dev);
if (!idev) { if (!idev) {
/* XXX: count this drop? */ /* XXX: count this drop? */
return; return reason;
} }
if (ipv6_chk_acast_addr(net, dev, &msg->target) || if (ipv6_chk_acast_addr(net, dev, &msg->target) ||
...@@ -924,9 +921,11 @@ static void ndisc_recv_ns(struct sk_buff *skb) ...@@ -924,9 +921,11 @@ static void ndisc_recv_ns(struct sk_buff *skb)
pneigh_enqueue(&nd_tbl, idev->nd_parms, n); pneigh_enqueue(&nd_tbl, idev->nd_parms, n);
goto out; goto out;
} }
} else } else {
SKB_DR_SET(reason, IPV6_NDISC_NS_OTHERHOST);
goto out; goto out;
} }
}
if (is_router < 0) if (is_router < 0)
is_router = idev->cnf.forwarding; is_router = idev->cnf.forwarding;
...@@ -958,6 +957,7 @@ static void ndisc_recv_ns(struct sk_buff *skb) ...@@ -958,6 +957,7 @@ static void ndisc_recv_ns(struct sk_buff *skb)
true, (ifp != NULL && inc), inc); true, (ifp != NULL && inc), inc);
if (neigh) if (neigh)
neigh_release(neigh); neigh_release(neigh);
reason = SKB_CONSUMED;
} }
out: out:
...@@ -965,6 +965,7 @@ static void ndisc_recv_ns(struct sk_buff *skb) ...@@ -965,6 +965,7 @@ static void ndisc_recv_ns(struct sk_buff *skb)
in6_ifa_put(ifp); in6_ifa_put(ifp);
else else
in6_dev_put(idev); in6_dev_put(idev);
return reason;
} }
static int accept_untracked_na(struct net_device *dev, struct in6_addr *saddr) static int accept_untracked_na(struct net_device *dev, struct in6_addr *saddr)
...@@ -986,7 +987,7 @@ static int accept_untracked_na(struct net_device *dev, struct in6_addr *saddr) ...@@ -986,7 +987,7 @@ static int accept_untracked_na(struct net_device *dev, struct in6_addr *saddr)
} }
} }
static void ndisc_recv_na(struct sk_buff *skb) static enum skb_drop_reason ndisc_recv_na(struct sk_buff *skb)
{ {
struct nd_msg *msg = (struct nd_msg *)skb_transport_header(skb); struct nd_msg *msg = (struct nd_msg *)skb_transport_header(skb);
struct in6_addr *saddr = &ipv6_hdr(skb)->saddr; struct in6_addr *saddr = &ipv6_hdr(skb)->saddr;
...@@ -999,22 +1000,21 @@ static void ndisc_recv_na(struct sk_buff *skb) ...@@ -999,22 +1000,21 @@ static void ndisc_recv_na(struct sk_buff *skb)
struct inet6_dev *idev = __in6_dev_get(dev); struct inet6_dev *idev = __in6_dev_get(dev);
struct inet6_ifaddr *ifp; struct inet6_ifaddr *ifp;
struct neighbour *neigh; struct neighbour *neigh;
SKB_DR(reason);
u8 new_state; u8 new_state;
if (skb->len < sizeof(struct nd_msg)) { if (skb->len < sizeof(struct nd_msg))
ND_PRINTK(2, warn, "NA: packet too short\n"); return SKB_DROP_REASON_PKT_TOO_SMALL;
return;
}
if (ipv6_addr_is_multicast(&msg->target)) { if (ipv6_addr_is_multicast(&msg->target)) {
ND_PRINTK(2, warn, "NA: target address is multicast\n"); ND_PRINTK(2, warn, "NA: target address is multicast\n");
return; return reason;
} }
if (ipv6_addr_is_multicast(daddr) && if (ipv6_addr_is_multicast(daddr) &&
msg->icmph.icmp6_solicited) { msg->icmph.icmp6_solicited) {
ND_PRINTK(2, warn, "NA: solicited NA is multicasted\n"); ND_PRINTK(2, warn, "NA: solicited NA is multicasted\n");
return; return reason;
} }
/* For some 802.11 wireless deployments (and possibly other networks), /* For some 802.11 wireless deployments (and possibly other networks),
...@@ -1024,18 +1024,17 @@ static void ndisc_recv_na(struct sk_buff *skb) ...@@ -1024,18 +1024,17 @@ static void ndisc_recv_na(struct sk_buff *skb)
*/ */
if (!msg->icmph.icmp6_solicited && idev && if (!msg->icmph.icmp6_solicited && idev &&
idev->cnf.drop_unsolicited_na) idev->cnf.drop_unsolicited_na)
return; return reason;
if (!ndisc_parse_options(dev, msg->opt, ndoptlen, &ndopts))
return SKB_DROP_REASON_IPV6_NDISC_BAD_OPTIONS;
if (!ndisc_parse_options(dev, msg->opt, ndoptlen, &ndopts)) {
ND_PRINTK(2, warn, "NS: invalid ND option\n");
return;
}
if (ndopts.nd_opts_tgt_lladdr) { if (ndopts.nd_opts_tgt_lladdr) {
lladdr = ndisc_opt_addr_data(ndopts.nd_opts_tgt_lladdr, dev); lladdr = ndisc_opt_addr_data(ndopts.nd_opts_tgt_lladdr, dev);
if (!lladdr) { if (!lladdr) {
ND_PRINTK(2, warn, ND_PRINTK(2, warn,
"NA: invalid link-layer address length\n"); "NA: invalid link-layer address length\n");
return; return reason;
} }
} }
ifp = ipv6_get_ifaddr(dev_net(dev), &msg->target, dev, 1); ifp = ipv6_get_ifaddr(dev_net(dev), &msg->target, dev, 1);
...@@ -1043,7 +1042,7 @@ static void ndisc_recv_na(struct sk_buff *skb) ...@@ -1043,7 +1042,7 @@ static void ndisc_recv_na(struct sk_buff *skb)
if (skb->pkt_type != PACKET_LOOPBACK if (skb->pkt_type != PACKET_LOOPBACK
&& (ifp->flags & IFA_F_TENTATIVE)) { && (ifp->flags & IFA_F_TENTATIVE)) {
addrconf_dad_failure(skb, ifp); addrconf_dad_failure(skb, ifp);
return; return reason;
} }
/* What should we make now? The advertisement /* What should we make now? The advertisement
is invalid, but ndisc specs say nothing is invalid, but ndisc specs say nothing
...@@ -1059,7 +1058,7 @@ static void ndisc_recv_na(struct sk_buff *skb) ...@@ -1059,7 +1058,7 @@ static void ndisc_recv_na(struct sk_buff *skb)
"NA: %pM advertised our address %pI6c on %s!\n", "NA: %pM advertised our address %pI6c on %s!\n",
eth_hdr(skb)->h_source, &ifp->addr, ifp->idev->dev->name); eth_hdr(skb)->h_source, &ifp->addr, ifp->idev->dev->name);
in6_ifa_put(ifp); in6_ifa_put(ifp);
return; return reason;
} }
neigh = neigh_lookup(&nd_tbl, &msg->target, dev); neigh = neigh_lookup(&nd_tbl, &msg->target, dev);
...@@ -1120,13 +1119,14 @@ static void ndisc_recv_na(struct sk_buff *skb) ...@@ -1120,13 +1119,14 @@ static void ndisc_recv_na(struct sk_buff *skb)
*/ */
rt6_clean_tohost(dev_net(dev), saddr); rt6_clean_tohost(dev_net(dev), saddr);
} }
reason = SKB_CONSUMED;
out: out:
neigh_release(neigh); neigh_release(neigh);
} }
return reason;
} }
static void ndisc_recv_rs(struct sk_buff *skb) static enum skb_drop_reason ndisc_recv_rs(struct sk_buff *skb)
{ {
struct rs_msg *rs_msg = (struct rs_msg *)skb_transport_header(skb); struct rs_msg *rs_msg = (struct rs_msg *)skb_transport_header(skb);
unsigned long ndoptlen = skb->len - sizeof(*rs_msg); unsigned long ndoptlen = skb->len - sizeof(*rs_msg);
...@@ -1135,14 +1135,15 @@ static void ndisc_recv_rs(struct sk_buff *skb) ...@@ -1135,14 +1135,15 @@ static void ndisc_recv_rs(struct sk_buff *skb)
const struct in6_addr *saddr = &ipv6_hdr(skb)->saddr; const struct in6_addr *saddr = &ipv6_hdr(skb)->saddr;
struct ndisc_options ndopts; struct ndisc_options ndopts;
u8 *lladdr = NULL; u8 *lladdr = NULL;
SKB_DR(reason);
if (skb->len < sizeof(*rs_msg)) if (skb->len < sizeof(*rs_msg))
return; return SKB_DROP_REASON_PKT_TOO_SMALL;
idev = __in6_dev_get(skb->dev); idev = __in6_dev_get(skb->dev);
if (!idev) { if (!idev) {
ND_PRINTK(1, err, "RS: can't find in6 device\n"); ND_PRINTK(1, err, "RS: can't find in6 device\n");
return; return reason;
} }
/* Don't accept RS if we're not in router mode */ /* Don't accept RS if we're not in router mode */
...@@ -1157,10 +1158,8 @@ static void ndisc_recv_rs(struct sk_buff *skb) ...@@ -1157,10 +1158,8 @@ static void ndisc_recv_rs(struct sk_buff *skb)
goto out; goto out;
/* Parse ND options */ /* Parse ND options */
if (!ndisc_parse_options(skb->dev, rs_msg->opt, ndoptlen, &ndopts)) { if (!ndisc_parse_options(skb->dev, rs_msg->opt, ndoptlen, &ndopts))
ND_PRINTK(2, notice, "NS: invalid ND option, ignored\n"); return SKB_DROP_REASON_IPV6_NDISC_BAD_OPTIONS;
goto out;
}
if (ndopts.nd_opts_src_lladdr) { if (ndopts.nd_opts_src_lladdr) {
lladdr = ndisc_opt_addr_data(ndopts.nd_opts_src_lladdr, lladdr = ndisc_opt_addr_data(ndopts.nd_opts_src_lladdr,
...@@ -1177,9 +1176,10 @@ static void ndisc_recv_rs(struct sk_buff *skb) ...@@ -1177,9 +1176,10 @@ static void ndisc_recv_rs(struct sk_buff *skb)
NEIGH_UPDATE_F_OVERRIDE_ISROUTER, NEIGH_UPDATE_F_OVERRIDE_ISROUTER,
NDISC_ROUTER_SOLICITATION, &ndopts); NDISC_ROUTER_SOLICITATION, &ndopts);
neigh_release(neigh); neigh_release(neigh);
reason = SKB_CONSUMED;
} }
out: out:
return; return reason;
} }
static void ndisc_ra_useropt(struct sk_buff *ra, struct nd_opt_hdr *opt) static void ndisc_ra_useropt(struct sk_buff *ra, struct nd_opt_hdr *opt)
...@@ -1228,20 +1228,21 @@ static void ndisc_ra_useropt(struct sk_buff *ra, struct nd_opt_hdr *opt) ...@@ -1228,20 +1228,21 @@ static void ndisc_ra_useropt(struct sk_buff *ra, struct nd_opt_hdr *opt)
rtnl_set_sk_err(net, RTNLGRP_ND_USEROPT, err); rtnl_set_sk_err(net, RTNLGRP_ND_USEROPT, err);
} }
static void ndisc_router_discovery(struct sk_buff *skb) static enum skb_drop_reason ndisc_router_discovery(struct sk_buff *skb)
{ {
struct ra_msg *ra_msg = (struct ra_msg *)skb_transport_header(skb); struct ra_msg *ra_msg = (struct ra_msg *)skb_transport_header(skb);
bool send_ifinfo_notify = false;
struct neighbour *neigh = NULL; struct neighbour *neigh = NULL;
struct inet6_dev *in6_dev; struct ndisc_options ndopts;
struct fib6_info *rt = NULL; struct fib6_info *rt = NULL;
struct inet6_dev *in6_dev;
u32 defrtr_usr_metric; u32 defrtr_usr_metric;
unsigned int pref = 0;
__u32 old_if_flags;
struct net *net; struct net *net;
SKB_DR(reason);
int lifetime; int lifetime;
struct ndisc_options ndopts;
int optlen; int optlen;
unsigned int pref = 0;
__u32 old_if_flags;
bool send_ifinfo_notify = false;
__u8 *opt = (__u8 *)(ra_msg + 1); __u8 *opt = (__u8 *)(ra_msg + 1);
...@@ -1253,17 +1254,15 @@ static void ndisc_router_discovery(struct sk_buff *skb) ...@@ -1253,17 +1254,15 @@ static void ndisc_router_discovery(struct sk_buff *skb)
__func__, skb->dev->name); __func__, skb->dev->name);
if (!(ipv6_addr_type(&ipv6_hdr(skb)->saddr) & IPV6_ADDR_LINKLOCAL)) { if (!(ipv6_addr_type(&ipv6_hdr(skb)->saddr) & IPV6_ADDR_LINKLOCAL)) {
ND_PRINTK(2, warn, "RA: source address is not link-local\n"); ND_PRINTK(2, warn, "RA: source address is not link-local\n");
return; return reason;
}
if (optlen < 0) {
ND_PRINTK(2, warn, "RA: packet too short\n");
return;
} }
if (optlen < 0)
return SKB_DROP_REASON_PKT_TOO_SMALL;
#ifdef CONFIG_IPV6_NDISC_NODETYPE #ifdef CONFIG_IPV6_NDISC_NODETYPE
if (skb->ndisc_nodetype == NDISC_NODETYPE_HOST) { if (skb->ndisc_nodetype == NDISC_NODETYPE_HOST) {
ND_PRINTK(2, warn, "RA: from host or unauthorized router\n"); ND_PRINTK(2, warn, "RA: from host or unauthorized router\n");
return; return reason;
} }
#endif #endif
...@@ -1275,13 +1274,11 @@ static void ndisc_router_discovery(struct sk_buff *skb) ...@@ -1275,13 +1274,11 @@ static void ndisc_router_discovery(struct sk_buff *skb)
if (!in6_dev) { if (!in6_dev) {
ND_PRINTK(0, err, "RA: can't find inet6 device for %s\n", ND_PRINTK(0, err, "RA: can't find inet6 device for %s\n",
skb->dev->name); skb->dev->name);
return; return reason;
} }
if (!ndisc_parse_options(skb->dev, opt, optlen, &ndopts)) { if (!ndisc_parse_options(skb->dev, opt, optlen, &ndopts))
ND_PRINTK(2, warn, "RA: invalid ND options\n"); return SKB_DROP_REASON_IPV6_NDISC_BAD_OPTIONS;
return;
}
if (!ipv6_accept_ra(in6_dev)) { if (!ipv6_accept_ra(in6_dev)) {
ND_PRINTK(2, info, ND_PRINTK(2, info,
...@@ -1362,7 +1359,7 @@ static void ndisc_router_discovery(struct sk_buff *skb) ...@@ -1362,7 +1359,7 @@ static void ndisc_router_discovery(struct sk_buff *skb)
"RA: %s got default router without neighbour\n", "RA: %s got default router without neighbour\n",
__func__); __func__);
fib6_info_release(rt); fib6_info_release(rt);
return; return reason;
} }
} }
/* Set default route metric as specified by user */ /* Set default route metric as specified by user */
...@@ -1387,7 +1384,7 @@ static void ndisc_router_discovery(struct sk_buff *skb) ...@@ -1387,7 +1384,7 @@ static void ndisc_router_discovery(struct sk_buff *skb)
ND_PRINTK(0, err, ND_PRINTK(0, err,
"RA: %s failed to add default route\n", "RA: %s failed to add default route\n",
__func__); __func__);
return; return reason;
} }
neigh = ip6_neigh_lookup(&rt->fib6_nh->fib_nh_gw6, neigh = ip6_neigh_lookup(&rt->fib6_nh->fib_nh_gw6,
...@@ -1398,7 +1395,7 @@ static void ndisc_router_discovery(struct sk_buff *skb) ...@@ -1398,7 +1395,7 @@ static void ndisc_router_discovery(struct sk_buff *skb)
"RA: %s got default router without neighbour\n", "RA: %s got default router without neighbour\n",
__func__); __func__);
fib6_info_release(rt); fib6_info_release(rt);
return; return reason;
} }
neigh->flags |= NTF_ROUTER; neigh->flags |= NTF_ROUTER;
} else if (rt && IPV6_EXTRACT_PREF(rt->fib6_flags) != pref) { } else if (rt && IPV6_EXTRACT_PREF(rt->fib6_flags) != pref) {
...@@ -1485,6 +1482,7 @@ static void ndisc_router_discovery(struct sk_buff *skb) ...@@ -1485,6 +1482,7 @@ static void ndisc_router_discovery(struct sk_buff *skb)
NEIGH_UPDATE_F_OVERRIDE_ISROUTER| NEIGH_UPDATE_F_OVERRIDE_ISROUTER|
NEIGH_UPDATE_F_ISROUTER, NEIGH_UPDATE_F_ISROUTER,
NDISC_ROUTER_ADVERTISEMENT, &ndopts); NDISC_ROUTER_ADVERTISEMENT, &ndopts);
reason = SKB_CONSUMED;
} }
if (!ipv6_accept_ra(in6_dev)) { if (!ipv6_accept_ra(in6_dev)) {
...@@ -1595,15 +1593,17 @@ static void ndisc_router_discovery(struct sk_buff *skb) ...@@ -1595,15 +1593,17 @@ static void ndisc_router_discovery(struct sk_buff *skb)
fib6_info_release(rt); fib6_info_release(rt);
if (neigh) if (neigh)
neigh_release(neigh); neigh_release(neigh);
return reason;
} }
static void ndisc_redirect_rcv(struct sk_buff *skb) static enum skb_drop_reason ndisc_redirect_rcv(struct sk_buff *skb)
{ {
u8 *hdr;
struct ndisc_options ndopts;
struct rd_msg *msg = (struct rd_msg *)skb_transport_header(skb); struct rd_msg *msg = (struct rd_msg *)skb_transport_header(skb);
u32 ndoptlen = skb_tail_pointer(skb) - (skb_transport_header(skb) + u32 ndoptlen = skb_tail_pointer(skb) - (skb_transport_header(skb) +
offsetof(struct rd_msg, opt)); offsetof(struct rd_msg, opt));
struct ndisc_options ndopts;
SKB_DR(reason);
u8 *hdr;
#ifdef CONFIG_IPV6_NDISC_NODETYPE #ifdef CONFIG_IPV6_NDISC_NODETYPE
switch (skb->ndisc_nodetype) { switch (skb->ndisc_nodetype) {
...@@ -1611,31 +1611,31 @@ static void ndisc_redirect_rcv(struct sk_buff *skb) ...@@ -1611,31 +1611,31 @@ static void ndisc_redirect_rcv(struct sk_buff *skb)
case NDISC_NODETYPE_NODEFAULT: case NDISC_NODETYPE_NODEFAULT:
ND_PRINTK(2, warn, ND_PRINTK(2, warn,
"Redirect: from host or unauthorized router\n"); "Redirect: from host or unauthorized router\n");
return; return reason;
} }
#endif #endif
if (!(ipv6_addr_type(&ipv6_hdr(skb)->saddr) & IPV6_ADDR_LINKLOCAL)) { if (!(ipv6_addr_type(&ipv6_hdr(skb)->saddr) & IPV6_ADDR_LINKLOCAL)) {
ND_PRINTK(2, warn, ND_PRINTK(2, warn,
"Redirect: source address is not link-local\n"); "Redirect: source address is not link-local\n");
return; return reason;
} }
if (!ndisc_parse_options(skb->dev, msg->opt, ndoptlen, &ndopts)) if (!ndisc_parse_options(skb->dev, msg->opt, ndoptlen, &ndopts))
return; return SKB_DROP_REASON_IPV6_NDISC_BAD_OPTIONS;
if (!ndopts.nd_opts_rh) { if (!ndopts.nd_opts_rh) {
ip6_redirect_no_header(skb, dev_net(skb->dev), ip6_redirect_no_header(skb, dev_net(skb->dev),
skb->dev->ifindex); skb->dev->ifindex);
return; return reason;
} }
hdr = (u8 *)ndopts.nd_opts_rh; hdr = (u8 *)ndopts.nd_opts_rh;
hdr += 8; hdr += 8;
if (!pskb_pull(skb, hdr - skb_transport_header(skb))) if (!pskb_pull(skb, hdr - skb_transport_header(skb)))
return; return SKB_DROP_REASON_PKT_TOO_SMALL;
icmpv6_notify(skb, NDISC_REDIRECT, 0, 0); return icmpv6_notify(skb, NDISC_REDIRECT, 0, 0);
} }
static void ndisc_fill_redirect_hdr_option(struct sk_buff *skb, static void ndisc_fill_redirect_hdr_option(struct sk_buff *skb,
...@@ -1781,8 +1781,9 @@ void ndisc_send_redirect(struct sk_buff *skb, const struct in6_addr *target) ...@@ -1781,8 +1781,9 @@ void ndisc_send_redirect(struct sk_buff *skb, const struct in6_addr *target)
static void pndisc_redo(struct sk_buff *skb) static void pndisc_redo(struct sk_buff *skb)
{ {
ndisc_recv_ns(skb); enum skb_drop_reason reason = ndisc_recv_ns(skb);
kfree_skb(skb);
kfree_skb_reason(skb, reason);
} }
static int ndisc_is_multicast(const void *pkey) static int ndisc_is_multicast(const void *pkey)
...@@ -1834,23 +1835,23 @@ enum skb_drop_reason ndisc_rcv(struct sk_buff *skb) ...@@ -1834,23 +1835,23 @@ enum skb_drop_reason ndisc_rcv(struct sk_buff *skb)
switch (msg->icmph.icmp6_type) { switch (msg->icmph.icmp6_type) {
case NDISC_NEIGHBOUR_SOLICITATION: case NDISC_NEIGHBOUR_SOLICITATION:
memset(NEIGH_CB(skb), 0, sizeof(struct neighbour_cb)); memset(NEIGH_CB(skb), 0, sizeof(struct neighbour_cb));
ndisc_recv_ns(skb); reason = ndisc_recv_ns(skb);
break; break;
case NDISC_NEIGHBOUR_ADVERTISEMENT: case NDISC_NEIGHBOUR_ADVERTISEMENT:
ndisc_recv_na(skb); reason = ndisc_recv_na(skb);
break; break;
case NDISC_ROUTER_SOLICITATION: case NDISC_ROUTER_SOLICITATION:
ndisc_recv_rs(skb); reason = ndisc_recv_rs(skb);
break; break;
case NDISC_ROUTER_ADVERTISEMENT: case NDISC_ROUTER_ADVERTISEMENT:
ndisc_router_discovery(skb); reason = ndisc_router_discovery(skb);
break; break;
case NDISC_REDIRECT: case NDISC_REDIRECT:
ndisc_redirect_rcv(skb); reason = ndisc_redirect_rcv(skb);
break; break;
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment