|
@@ -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,
|
|
@@ -762,6 +745,7 @@ static void flush_stack(struct sock **stack, unsigned int count,
|
|
|
|
|
|
if (skb1 && udpv6_queue_rcv_skb(sk, skb1) <= 0)
|
|
|
skb1 = NULL;
|
|
|
+ sock_put(sk);
|
|
|
}
|
|
|
if (unlikely(skb1))
|
|
|
kfree_skb(skb1);
|
|
@@ -787,43 +771,51 @@ 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;
|
|
|
- unsigned int i, count = 0;
|
|
|
+ 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 count = 0, offset = offsetof(typeof(*sk), sk_nulls_node);
|
|
|
+ unsigned int hash2 = 0, hash2_any = 0, use_hash2 = (hslot->count > 10);
|
|
|
+
|
|
|
+ if (use_hash2) {
|
|
|
+ hash2_any = udp6_portaddr_hash(net, &in6addr_any, hnum) &
|
|
|
+ udp_table.mask;
|
|
|
+ hash2 = udp6_portaddr_hash(net, daddr, hnum) & udp_table.mask;
|
|
|
+start_lookup:
|
|
|
+ hslot = &udp_table.hash2[hash2];
|
|
|
+ offset = offsetof(typeof(*sk), __sk_common.skc_portaddr_node);
|
|
|
+ }
|
|
|
|
|
|
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_entry_offset(sk, node, &hslot->head, offset) {
|
|
|
+ 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;
|
|
|
+ sock_hold(sk);
|
|
|
}
|
|
|
}
|
|
|
- /*
|
|
|
- * before releasing the lock, we must take reference on sockets
|
|
|
- */
|
|
|
- for (i = 0; i < count; i++)
|
|
|
- sock_hold(stack[i]);
|
|
|
|
|
|
spin_unlock(&hslot->lock);
|
|
|
|
|
|
+ /* Also lookup *:port if we are using hash2 and haven't done so yet. */
|
|
|
+ if (use_hash2 && hash2 != hash2_any) {
|
|
|
+ hash2 = hash2_any;
|
|
|
+ goto start_lookup;
|
|
|
+ }
|
|
|
+
|
|
|
if (count) {
|
|
|
flush_stack(stack, count, skb, count - 1);
|
|
|
-
|
|
|
- for (i = 0; i < count; i++)
|
|
|
- sock_put(stack[i]);
|
|
|
} else {
|
|
|
kfree_skb(skb);
|
|
|
}
|