Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

udp: Increment UDP_MIB_IGNOREDMULTI for arriving unmatched multicasts

As NIC multicast filtering isn't perfect, and some platforms are
quite content to spew broadcasts, we should not trigger an event
for skb:kfree_skb when we do not have a match for such an incoming
datagram. We do though want to avoid sweeping the matter under the
rug entirely, so increment a suitable statistic.

This incorporates feedback from David L. Stevens, Karl Neiss and Eric
Dumazet.

V3 - use bool per David Miller

Signed-off-by: Rick Jones <rick.jones2@hp.com>
Signed-off-by: David S. Miller <davem@davemloft.net>

authored by

Rick Jones and committed by
David S. Miller
36cbb245 f46ad73a

+20 -6
+1
include/uapi/linux/snmp.h
··· 156 156 UDP_MIB_RCVBUFERRORS, /* RcvbufErrors */ 157 157 UDP_MIB_SNDBUFERRORS, /* SndbufErrors */ 158 158 UDP_MIB_CSUMERRORS, /* InCsumErrors */ 159 + UDP_MIB_IGNOREDMULTI, /* IgnoredMulti */ 159 160 __UDP_MIB_MAX 160 161 }; 161 162
+1
net/ipv4/proc.c
··· 181 181 SNMP_MIB_ITEM("RcvbufErrors", UDP_MIB_RCVBUFERRORS), 182 182 SNMP_MIB_ITEM("SndbufErrors", UDP_MIB_SNDBUFERRORS), 183 183 SNMP_MIB_ITEM("InCsumErrors", UDP_MIB_CSUMERRORS), 184 + SNMP_MIB_ITEM("IgnoredMulti", UDP_MIB_IGNOREDMULTI), 184 185 SNMP_MIB_SENTINEL 185 186 }; 186 187
+9 -3
net/ipv4/udp.c
··· 1647 1647 static int __udp4_lib_mcast_deliver(struct net *net, struct sk_buff *skb, 1648 1648 struct udphdr *uh, 1649 1649 __be32 saddr, __be32 daddr, 1650 - struct udp_table *udptable) 1650 + struct udp_table *udptable, 1651 + int proto) 1651 1652 { 1652 1653 struct sock *sk, *stack[256 / sizeof(struct sock *)]; 1653 1654 struct hlist_nulls_node *node; ··· 1657 1656 int dif = skb->dev->ifindex; 1658 1657 unsigned int count = 0, offset = offsetof(typeof(*sk), sk_nulls_node); 1659 1658 unsigned int hash2 = 0, hash2_any = 0, use_hash2 = (hslot->count > 10); 1659 + bool inner_flushed = false; 1660 1660 1661 1661 if (use_hash2) { 1662 1662 hash2_any = udp4_portaddr_hash(net, htonl(INADDR_ANY), hnum) & ··· 1676 1674 dif, hnum)) { 1677 1675 if (unlikely(count == ARRAY_SIZE(stack))) { 1678 1676 flush_stack(stack, count, skb, ~0); 1677 + inner_flushed = true; 1679 1678 count = 0; 1680 1679 } 1681 1680 stack[count++] = sk; ··· 1698 1695 if (count) { 1699 1696 flush_stack(stack, count, skb, count - 1); 1700 1697 } else { 1701 - kfree_skb(skb); 1698 + if (!inner_flushed) 1699 + UDP_INC_STATS_BH(net, UDP_MIB_IGNOREDMULTI, 1700 + proto == IPPROTO_UDPLITE); 1701 + consume_skb(skb); 1702 1702 } 1703 1703 return 0; 1704 1704 } ··· 1787 1781 1788 1782 if (rt->rt_flags & (RTCF_BROADCAST|RTCF_MULTICAST)) 1789 1783 return __udp4_lib_mcast_deliver(net, skb, uh, 1790 - saddr, daddr, udptable); 1784 + saddr, daddr, udptable, proto); 1791 1785 1792 1786 sk = __udp4_lib_lookup_skb(skb, uh->source, uh->dest, udptable); 1793 1787 if (sk != NULL) {
+1
net/ipv6/proc.c
··· 136 136 SNMP_MIB_ITEM("Udp6RcvbufErrors", UDP_MIB_RCVBUFERRORS), 137 137 SNMP_MIB_ITEM("Udp6SndbufErrors", UDP_MIB_SNDBUFERRORS), 138 138 SNMP_MIB_ITEM("Udp6InCsumErrors", UDP_MIB_CSUMERRORS), 139 + SNMP_MIB_ITEM("Udp6IgnoredMulti", UDP_MIB_IGNOREDMULTI), 139 140 SNMP_MIB_SENTINEL 140 141 }; 141 142
+8 -3
net/ipv6/udp.c
··· 771 771 */ 772 772 static int __udp6_lib_mcast_deliver(struct net *net, struct sk_buff *skb, 773 773 const struct in6_addr *saddr, const struct in6_addr *daddr, 774 - struct udp_table *udptable) 774 + struct udp_table *udptable, int proto) 775 775 { 776 776 struct sock *sk, *stack[256 / sizeof(struct sock *)]; 777 777 const struct udphdr *uh = udp_hdr(skb); ··· 781 781 int dif = inet6_iif(skb); 782 782 unsigned int count = 0, offset = offsetof(typeof(*sk), sk_nulls_node); 783 783 unsigned int hash2 = 0, hash2_any = 0, use_hash2 = (hslot->count > 10); 784 + bool inner_flushed = false; 784 785 785 786 if (use_hash2) { 786 787 hash2_any = udp6_portaddr_hash(net, &in6addr_any, hnum) & ··· 804 803 (uh->check || udp_sk(sk)->no_check6_rx)) { 805 804 if (unlikely(count == ARRAY_SIZE(stack))) { 806 805 flush_stack(stack, count, skb, ~0); 806 + inner_flushed = true; 807 807 count = 0; 808 808 } 809 809 stack[count++] = sk; ··· 823 821 if (count) { 824 822 flush_stack(stack, count, skb, count - 1); 825 823 } else { 826 - kfree_skb(skb); 824 + if (!inner_flushed) 825 + UDP_INC_STATS_BH(net, UDP_MIB_IGNOREDMULTI, 826 + proto == IPPROTO_UDPLITE); 827 + consume_skb(skb); 827 828 } 828 829 return 0; 829 830 } ··· 878 873 */ 879 874 if (ipv6_addr_is_multicast(daddr)) 880 875 return __udp6_lib_mcast_deliver(net, skb, 881 - saddr, daddr, udptable); 876 + saddr, daddr, udptable, proto); 882 877 883 878 /* Unicast */ 884 879