|
@@ -702,43 +702,26 @@ drop:
|
|
|
return -1;
|
|
|
}
|
|
|
|
|
|
-static struct sock *udp_v6_mcast_next(struct net *net, struct sock *sk,
|
|
|
- __be16 loc_port, const struct in6_addr *loc_addr,
|
|
|
- __be16 rmt_port, const struct in6_addr *rmt_addr,
|
|
|
- int dif)
|
|
|
+static bool __udp_v6_is_mcast_sock(struct net *net, struct sock *sk,
|
|
|
+ __be16 loc_port, const struct in6_addr *loc_addr,
|
|
|
+ __be16 rmt_port, const struct in6_addr *rmt_addr,
|
|
|
+ int dif, unsigned short hnum)
|
|
|
{
|
|
|
- struct hlist_nulls_node *node;
|
|
|
- unsigned short num = ntohs(loc_port);
|
|
|
-
|
|
|
- sk_nulls_for_each_from(sk, node) {
|
|
|
- struct inet_sock *inet = inet_sk(sk);
|
|
|
-
|
|
|
- if (!net_eq(sock_net(sk), net))
|
|
|
- continue;
|
|
|
-
|
|
|
- if (udp_sk(sk)->udp_port_hash == num &&
|
|
|
- sk->sk_family == PF_INET6) {
|
|
|
- if (inet->inet_dport) {
|
|
|
- if (inet->inet_dport != rmt_port)
|
|
|
- continue;
|
|
|
- }
|
|
|
- if (!ipv6_addr_any(&sk->sk_v6_daddr) &&
|
|
|
- !ipv6_addr_equal(&sk->sk_v6_daddr, rmt_addr))
|
|
|
- continue;
|
|
|
-
|
|
|
- if (sk->sk_bound_dev_if && sk->sk_bound_dev_if != dif)
|
|
|
- continue;
|
|
|
+ struct inet_sock *inet = inet_sk(sk);
|
|
|
|
|
|
- if (!ipv6_addr_any(&sk->sk_v6_rcv_saddr)) {
|
|
|
- if (!ipv6_addr_equal(&sk->sk_v6_rcv_saddr, loc_addr))
|
|
|
- continue;
|
|
|
- }
|
|
|
- if (!inet6_mc_check(sk, loc_addr, rmt_addr))
|
|
|
- continue;
|
|
|
- return sk;
|
|
|
- }
|
|
|
- }
|
|
|
- return NULL;
|
|
|
+ if (!net_eq(sock_net(sk), net))
|
|
|
+ return false;
|
|
|
+
|
|
|
+ if (udp_sk(sk)->udp_port_hash != hnum ||
|
|
|
+ sk->sk_family != PF_INET6 ||
|
|
|
+ (inet->inet_dport && inet->inet_dport != rmt_port) ||
|
|
|
+ (!ipv6_addr_any(&sk->sk_v6_daddr) &&
|
|
|
+ !ipv6_addr_equal(&sk->sk_v6_daddr, rmt_addr)) ||
|
|
|
+ (sk->sk_bound_dev_if && sk->sk_bound_dev_if != dif))
|
|
|
+ return false;
|
|
|
+ if (!inet6_mc_check(sk, loc_addr, rmt_addr))
|
|
|
+ return false;
|
|
|
+ return true;
|
|
|
}
|
|
|
|
|
|
static void flush_stack(struct sock **stack, unsigned int count,
|
|
@@ -787,28 +770,27 @@ static int __udp6_lib_mcast_deliver(struct net *net, struct sk_buff *skb,
|
|
|
{
|
|
|
struct sock *sk, *stack[256 / sizeof(struct sock *)];
|
|
|
const struct udphdr *uh = udp_hdr(skb);
|
|
|
- struct udp_hslot *hslot = udp_hashslot(udptable, net, ntohs(uh->dest));
|
|
|
- int dif;
|
|
|
+ struct hlist_nulls_node *node;
|
|
|
+ unsigned short hnum = ntohs(uh->dest);
|
|
|
+ struct udp_hslot *hslot = udp_hashslot(udptable, net, hnum);
|
|
|
+ int dif = inet6_iif(skb);
|
|
|
unsigned int i, count = 0;
|
|
|
|
|
|
spin_lock(&hslot->lock);
|
|
|
- sk = sk_nulls_head(&hslot->head);
|
|
|
- dif = inet6_iif(skb);
|
|
|
- sk = udp_v6_mcast_next(net, sk, uh->dest, daddr, uh->source, saddr, dif);
|
|
|
- while (sk) {
|
|
|
- /* If zero checksum and no_check is not on for
|
|
|
- * the socket then skip it.
|
|
|
- */
|
|
|
- if (uh->check || udp_sk(sk)->no_check6_rx)
|
|
|
+ sk_nulls_for_each(sk, node, &hslot->head) {
|
|
|
+ if (__udp_v6_is_mcast_sock(net, sk,
|
|
|
+ uh->dest, daddr,
|
|
|
+ uh->source, saddr,
|
|
|
+ dif, hnum) &&
|
|
|
+ /* If zero checksum and no_check is not on for
|
|
|
+ * the socket then skip it.
|
|
|
+ */
|
|
|
+ (uh->check || udp_sk(sk)->no_check6_rx)) {
|
|
|
+ if (unlikely(count == ARRAY_SIZE(stack))) {
|
|
|
+ flush_stack(stack, count, skb, ~0);
|
|
|
+ count = 0;
|
|
|
+ }
|
|
|
stack[count++] = sk;
|
|
|
-
|
|
|
- sk = udp_v6_mcast_next(net, sk_nulls_next(sk), uh->dest, daddr,
|
|
|
- uh->source, saddr, dif);
|
|
|
- if (unlikely(count == ARRAY_SIZE(stack))) {
|
|
|
- if (!sk)
|
|
|
- break;
|
|
|
- flush_stack(stack, count, skb, ~0);
|
|
|
- count = 0;
|
|
|
}
|
|
|
}
|
|
|
/*
|