From: Alan Maguire Date: Mon, 23 Jan 2017 15:28:35 +0000 (+0000) Subject: dtrace: add ip SDT provider X-Git-Tag: v4.1.12-98.0.20170517_2143~41^2~6 X-Git-Url: https://www.infradead.org/git/?a=commitdiff_plain;h=7d928e34aa0e7b310b50ed7a70409017c4e125e4;p=users%2Fjedix%2Flinux-maple.git dtrace: add ip SDT provider This gives probe points ip:::receive, ip:::send, ip:::drop-in, and ip:::drop-out, with parameters compatible with the Solaris implementation. Signed-off-by: Alan Maguire Acked-by: Nick Alcock Reviewed-by: HÃ¥kon Bugge Orabug: 25557554 --- diff --git a/net/ipv4/ip_input.c b/net/ipv4/ip_input.c index 2db4c8773c1b..e3b756ab7ae6 100644 --- a/net/ipv4/ip_input.c +++ b/net/ipv4/ip_input.c @@ -146,6 +146,7 @@ #include #include #include +#include /* * Process Router Attention IP option (RFC 2113) @@ -244,15 +245,25 @@ static int ip_local_deliver_finish(struct sock *sk, struct sk_buff *skb) */ int ip_local_deliver(struct sk_buff *skb) { + struct iphdr *iph = ip_hdr(skb); + /* * Reassemble IP fragments. */ - if (ip_is_fragment(ip_hdr(skb))) { + if (ip_is_fragment(iph)) { if (ip_defrag(skb, IP_DEFRAG_LOCAL_DELIVER)) return 0; } + DTRACE_IP(receive, + struct sk_buff * : pktinfo_t *, skb, + struct sock * : csinfo_t *, skb->sk, + void_ip_t * : ipinfo_t *, iph, + struct net_device * : ifinfo_t *, skb->dev, + struct iphdr * : ipv4info_t *, iph, + struct ipv6hdr * : ipv6info_t *, NULL); + return NF_HOOK(NFPROTO_IPV4, NF_INET_LOCAL_IN, NULL, skb, skb->dev, NULL, ip_local_deliver_finish); @@ -261,8 +272,9 @@ int ip_local_deliver(struct sk_buff *skb) static inline bool ip_rcv_options(struct sk_buff *skb) { struct ip_options *opt; - const struct iphdr *iph; + const struct iphdr *iph = NULL; struct net_device *dev = skb->dev; + const char *dropreason; /* It looks as overkill, because not all IP options require packet mangling. @@ -272,6 +284,7 @@ static inline bool ip_rcv_options(struct sk_buff *skb) --ANK (980813) */ if (skb_cow(skb, skb_headroom(skb))) { + dropreason = "copy-on-write failed"; IP_INC_STATS_BH(dev_net(dev), IPSTATS_MIB_INDISCARDS); goto drop; } @@ -281,6 +294,7 @@ static inline bool ip_rcv_options(struct sk_buff *skb) opt->optlen = iph->ihl*4 - sizeof(struct iphdr); if (ip_options_compile(dev_net(dev), opt, skb)) { + dropreason = "invalid options"; IP_INC_STATS_BH(dev_net(dev), IPSTATS_MIB_INHDRERRORS); goto drop; } @@ -294,16 +308,28 @@ static inline bool ip_rcv_options(struct sk_buff *skb) net_info_ratelimited("source route option %pI4 -> %pI4\n", &iph->saddr, &iph->daddr); + dropreason = "invalid source route options"; goto drop; } } - if (ip_options_rcv_srr(skb)) + if (ip_options_rcv_srr(skb)) { + dropreason = "invalid options"; goto drop; + } } return false; drop: + DTRACE_IP(drop__in, + struct sk_buff * : pktinfo_t *, skb, + struct sock * : csinfo_t *, skb->sk, + void_ip_t * : ipinfo_t *, iph, + struct net_device * : ifinfo_t *, skb->dev, + struct iphdr * : ipv4info_t *, iph, + struct ipv6hdr * : ipv6info_t *, NULL, + char * : string, dropreason); + return true; } @@ -376,8 +402,9 @@ drop: */ int ip_rcv(struct sk_buff *skb, struct net_device *dev, struct packet_type *pt, struct net_device *orig_dev) { - const struct iphdr *iph; + const struct iphdr *iph = NULL; u32 len; + const char *dropreason = "header invalid"; /* When the interface is in promisc. mode, drop all the crap * that it receives, do not try to analyse it. @@ -390,8 +417,9 @@ int ip_rcv(struct sk_buff *skb, struct net_device *dev, struct packet_type *pt, skb = skb_share_check(skb, GFP_ATOMIC); if (!skb) { + dropreason = "could not clone shared buffer"; IP_INC_STATS_BH(dev_net(dev), IPSTATS_MIB_INDISCARDS); - goto out; + goto drop; } if (!pskb_may_pull(skb, sizeof(struct iphdr))) @@ -430,6 +458,7 @@ int ip_rcv(struct sk_buff *skb, struct net_device *dev, struct packet_type *pt, len = ntohs(iph->tot_len); if (skb->len < len) { + dropreason = "packet too short"; IP_INC_STATS_BH(dev_net(dev), IPSTATS_MIB_INTRUNCATEDPKTS); goto drop; } else if (len < (iph->ihl*4)) @@ -440,6 +469,7 @@ int ip_rcv(struct sk_buff *skb, struct net_device *dev, struct packet_type *pt, * Note this now means skb->len holds ntohs(iph->tot_len). */ if (pskb_trim_rcsum(skb, len)) { + dropreason = "could not trim buffer"; IP_INC_STATS_BH(dev_net(dev), IPSTATS_MIB_INDISCARDS); goto drop; } @@ -458,10 +488,20 @@ int ip_rcv(struct sk_buff *skb, struct net_device *dev, struct packet_type *pt, csum_error: IP_INC_STATS_BH(dev_net(dev), IPSTATS_MIB_CSUMERRORS); + dropreason = "checksum error"; inhdr_error: IP_INC_STATS_BH(dev_net(dev), IPSTATS_MIB_INHDRERRORS); + drop: + DTRACE_IP(drop__in, + struct sk_buff * : pktinfo_t *, skb, + struct sock * : csinfo_t *, skb ? skb->sk : NULL, + void_ip_t * : ipinfo_t *, iph, + struct net_device * : ifinfo_t *, dev, + struct iphdr * : ipv4info_t *, iph, + void * : ipv6info_t *, NULL, + char * : string, dropreason); + kfree_skb(skb); -out: return NET_RX_DROP; } diff --git a/net/ipv4/ip_output.c b/net/ipv4/ip_output.c index fe16f418e775..3326ba9c0428 100644 --- a/net/ipv4/ip_output.c +++ b/net/ipv4/ip_output.c @@ -79,6 +79,7 @@ #include #include #include +#include int sysctl_ip_default_ttl __read_mostly = IPDEFTTL; EXPORT_SYMBOL(sysctl_ip_default_ttl); @@ -97,6 +98,15 @@ int __ip_local_out_sk(struct sock *sk, struct sk_buff *skb) iph->tot_len = htons(skb->len); ip_send_check(iph); + + DTRACE_IP(send, + struct sk_buff * : pktinfo_t *, skb, + struct sock * : csinfo_t *, sk, + void_ip_t * : ipinfo_t *, iph, + struct net_device * : ifinfo_t *, skb->dev, + struct iphdr * : ipv4info_t *, iph, + struct ipv6hdr * : ipv6info_t *, NULL); + return nf_hook(NFPROTO_IPV4, NF_INET_LOCAL_OUT, sk, skb, NULL, skb_dst(skb)->dev, dst_output_sk); } @@ -858,6 +868,7 @@ static int __ip_append_data(struct sock *sk, int csummode = CHECKSUM_NONE; struct rtable *rt = (struct rtable *)cork->dst; u32 tskey = 0; + const char *dropreason; skb = skb_peek_tail(queue); @@ -874,9 +885,13 @@ static int __ip_append_data(struct sock *sk, maxnonfragsize = ip_sk_ignore_df(sk) ? 0xFFFF : mtu; if (cork->length + length > maxnonfragsize - fragheaderlen) { + struct iphdr *iph = ip_hdr(skb); + ip_local_error(sk, EMSGSIZE, fl4->daddr, inet->inet_dport, mtu - (opt ? opt->optlen : 0)); - return -EMSGSIZE; + dropreason = "packet too big"; + err = -EMSGSIZE; + goto error2; } /* @@ -897,8 +912,10 @@ static int __ip_append_data(struct sock *sk, err = ip_ufo_append_data(sk, queue, getfrag, from, length, hh_len, fragheaderlen, transhdrlen, maxfraglen, flags); - if (err) + if (err) { + dropreason = "could not offload UDP fragmentation"; goto error; + } return 0; } @@ -967,8 +984,10 @@ alloc_new_skb: skb = sock_wmalloc(sk, alloclen + hh_len + 15, 1, sk->sk_allocation); - if (unlikely(!skb)) + if (unlikely(!skb)) { + dropreason = "no buffers"; err = -ENOBUFS; + } } if (!skb) goto error; @@ -1008,7 +1027,9 @@ alloc_new_skb: copy = datalen - transhdrlen - fraggap; if (copy > 0 && getfrag(from, data + transhdrlen, offset, copy, fraggap, skb) < 0) { err = -EFAULT; + dropreason = "could not fragment packet"; kfree_skb(skb); + skb = NULL; goto error; } @@ -1035,6 +1056,7 @@ alloc_new_skb: if (getfrag(from, skb_put(skb, copy), offset, copy, off, skb) < 0) { __skb_trim(skb, off); + dropreason = "could not fragment packet"; err = -EFAULT; goto error; } @@ -1042,14 +1064,18 @@ alloc_new_skb: int i = skb_shinfo(skb)->nr_frags; err = -ENOMEM; - if (!sk_page_frag_refill(sk, pfrag)) + if (!sk_page_frag_refill(sk, pfrag)) { + dropreason = "no memory"; goto error; + } if (!skb_can_coalesce(skb, i, pfrag->page, pfrag->offset)) { err = -EMSGSIZE; - if (i == MAX_SKB_FRAGS) + if (i == MAX_SKB_FRAGS) { + dropreason = "too many fragments"; goto error; + } __skb_fill_page_desc(skb, i, pfrag->page, pfrag->offset, 0); @@ -1059,8 +1085,10 @@ alloc_new_skb: copy = min_t(int, copy, pfrag->size - pfrag->offset); if (getfrag(from, page_address(pfrag->page) + pfrag->offset, - offset, copy, skb->len, skb) < 0) + offset, copy, skb->len, skb) < 0) { + dropreason = "could not framgent packet"; goto error_efault; + } pfrag->offset += copy; skb_frag_size_add(&skb_shinfo(skb)->frags[i - 1], copy); @@ -1080,6 +1108,16 @@ error_efault: error: cork->length -= length; IP_INC_STATS(sock_net(sk), IPSTATS_MIB_OUTDISCARDS); +error2: + DTRACE_IP(drop__out, + struct sk_buff * : pktinfo_t *, skb, + struct sock * : csinfo_t *, skb ? skb->sk : NULL, + void_ip_t * : ipinfo_t *, skb ? ip_hdr(skb) : NULL, + struct net_device * : ifinfo_t *, skb ? skb->dev : NULL, + struct iphdr * : ipv4info_t *, skb ? ip_hdr(skb) : NULL, + struct ipv6hdr * : ipv6info_t *, NULL, + char * : string, dropreason); + return err; } @@ -1173,6 +1211,8 @@ ssize_t ip_append_page(struct sock *sk, struct flowi4 *fl4, struct page *page, int len; int err; unsigned int maxfraglen, fragheaderlen, fraggap, maxnonfragsize; + struct iphdr *iph; + const char *dropreason; if (inet->hdrincl) return -EPERM; @@ -1238,6 +1278,7 @@ ssize_t ip_append_page(struct sock *sk, struct flowi4 *fl4, struct page *page, alloclen = fragheaderlen + hh_len + fraggap + 15; skb = sock_wmalloc(sk, alloclen, 1, sk->sk_allocation); if (unlikely(!skb)) { + dropreason = "no buffers"; err = -ENOBUFS; goto error; } @@ -1282,6 +1323,7 @@ ssize_t ip_append_page(struct sock *sk, struct flowi4 *fl4, struct page *page, get_page(page); skb_fill_page_desc(skb, i, page, offset, len); } else { + dropreason = "packet too big"; err = -EMSGSIZE; goto error; } @@ -1304,6 +1346,16 @@ ssize_t ip_append_page(struct sock *sk, struct flowi4 *fl4, struct page *page, error: cork->length -= size; IP_INC_STATS(sock_net(sk), IPSTATS_MIB_OUTDISCARDS); + iph = skb ? ip_hdr(skb) : NULL; + DTRACE_IP(drop__out, + struct sk_buff * : pktinfo_t *, skb, + struct sock * : csinfo_t *, skb ? skb->sk : NULL, + void_ip_t * : ipinfo_t *, iph, + struct net_device * : ifinfo_t *, skb ? skb->dev : NULL, + struct iphdr * : ipv4info_t *, iph, + struct ipv6hdr * : ipv6info_t *, NULL, + char * : string, dropreason); + return err; } @@ -1420,8 +1472,21 @@ int ip_send_skb(struct net *net, struct sk_buff *skb) if (err) { if (err > 0) err = net_xmit_errno(err); - if (err) + if (err) { + struct iphdr *iph = ip_hdr(skb); + IP_INC_STATS(net, IPSTATS_MIB_OUTDISCARDS); + DTRACE_IP(drop__out, + struct sk_buff * : pktinfo_t *, skb, + struct sock * : csinfo_t *, skb->sk, + void_ip_t * : ipinfo_t *, iph, + struct net_device * : ifinfo_t *, skb->dev, + struct iphdr * : ipv4info_t *, iph, + struct ipv6hdr * : ipv6info_t *, NULL, + char * : string, "packet too short"); + } + + } return err; diff --git a/net/ipv4/raw.c b/net/ipv4/raw.c index c77aac75759d..3e24498e8ffc 100644 --- a/net/ipv4/raw.c +++ b/net/ipv4/raw.c @@ -79,6 +79,7 @@ #include #include #include +#include struct raw_frag_vec { struct msghdr *msg; @@ -411,6 +412,14 @@ static int raw_send_hdrinc(struct sock *sk, struct flowi4 *fl4, icmp_out_count(net, ((struct icmphdr *) skb_transport_header(skb))->type); + DTRACE_IP(send, + struct sk_buff * : pktinfo_t *, skb, + struct sock * : csinfo_t *, sk, + void_ip_t * : ipinfo_t *, iph, + struct net_device * : ifinfo_t *, skb->dev, + struct iphdr * : ipv4info_t *, iph, + struct ipv6hdr * : ipv6info_t *, NULL); + err = NF_HOOK(NFPROTO_IPV4, NF_INET_LOCAL_OUT, sk, skb, NULL, rt->dst.dev, dst_output_sk); if (err > 0) diff --git a/net/ipv6/ip6_input.c b/net/ipv6/ip6_input.c index 57990c929cd8..cfbbca384b46 100644 --- a/net/ipv6/ip6_input.c +++ b/net/ipv6/ip6_input.c @@ -45,6 +45,7 @@ #include #include #include +#include int ip6_rcv_finish(struct sock *sk, struct sk_buff *skb) { @@ -63,10 +64,11 @@ int ip6_rcv_finish(struct sock *sk, struct sk_buff *skb) int ipv6_rcv(struct sk_buff *skb, struct net_device *dev, struct packet_type *pt, struct net_device *orig_dev) { - const struct ipv6hdr *hdr; + const struct ipv6hdr *hdr = NULL; u32 pkt_len; struct inet6_dev *idev; struct net *net = dev_net(skb->dev); + const char *dropreason = "header invalid"; if (skb->pkt_type == PACKET_OTHERHOST) { kfree_skb(skb); @@ -82,6 +84,7 @@ int ipv6_rcv(struct sk_buff *skb, struct net_device *dev, struct packet_type *pt if ((skb = skb_share_check(skb, GFP_ATOMIC)) == NULL || !idev || unlikely(idev->cnf.disable_ipv6)) { IP6_INC_STATS_BH(net, idev, IPSTATS_MIB_INDISCARDS); + dropreason = "could not clone shared buffer"; goto drop; } @@ -100,8 +103,10 @@ int ipv6_rcv(struct sk_buff *skb, struct net_device *dev, struct packet_type *pt */ IP6CB(skb)->iif = skb_dst(skb) ? ip6_dst_idev(skb_dst(skb))->dev->ifindex : dev->ifindex; - if (unlikely(!pskb_may_pull(skb, sizeof(*hdr)))) + if (unlikely(!pskb_may_pull(skb, sizeof(*hdr)))) { + hdr = ipv6_hdr(skb); goto err; + } hdr = ipv6_hdr(skb); @@ -118,8 +123,10 @@ int ipv6_rcv(struct sk_buff *skb, struct net_device *dev, struct packet_type *pt * of loopback must be dropped. */ if (!(dev->flags & IFF_LOOPBACK) && - ipv6_addr_loopback(&hdr->daddr)) + ipv6_addr_loopback(&hdr->daddr)) { + dropreason = "loopback destination received on inteface"; goto err; + } /* RFC4291 Errata ID: 3480 * Interface-Local scope spans only a single interface on a @@ -130,8 +137,10 @@ int ipv6_rcv(struct sk_buff *skb, struct net_device *dev, struct packet_type *pt if (!(skb->pkt_type == PACKET_LOOPBACK || dev->flags & IFF_LOOPBACK) && ipv6_addr_is_multicast(&hdr->daddr) && - IPV6_ADDR_MC_SCOPE(&hdr->daddr) == 1) + IPV6_ADDR_MC_SCOPE(&hdr->daddr) == 1) { + dropreason = "interface-local scope received from other node"; goto err; + } /* RFC4291 2.7 * Nodes must not originate a packet to a multicast address whose scope @@ -139,16 +148,21 @@ int ipv6_rcv(struct sk_buff *skb, struct net_device *dev, struct packet_type *pt * must be silently dropped. */ if (ipv6_addr_is_multicast(&hdr->daddr) && - IPV6_ADDR_MC_SCOPE(&hdr->daddr) == 0) + IPV6_ADDR_MC_SCOPE(&hdr->daddr) == 0) { + dropreason = + "packet to multicast address with reserved scope 0"; goto err; + } /* * RFC4291 2.7 * Multicast addresses must not be used as source addresses in IPv6 * packets or appear in any Routing header. */ - if (ipv6_addr_is_multicast(&hdr->saddr)) + if (ipv6_addr_is_multicast(&hdr->saddr)) { + dropreason = "multicast source address in IPv6 packet"; goto err; + } skb->transport_header = skb->network_header + sizeof(*hdr); IP6CB(skb)->nhoff = offsetof(struct ipv6hdr, nexthdr); @@ -160,10 +174,12 @@ int ipv6_rcv(struct sk_buff *skb, struct net_device *dev, struct packet_type *pt if (pkt_len + sizeof(struct ipv6hdr) > skb->len) { IP6_INC_STATS_BH(net, idev, IPSTATS_MIB_INTRUNCATEDPKTS); + dropreason = "packet too short"; goto drop; } if (pskb_trim_rcsum(skb, pkt_len + sizeof(struct ipv6hdr))) { IP6_INC_STATS_BH(net, idev, IPSTATS_MIB_INHDRERRORS); + dropreason = "could not trim buffer"; goto drop; } hdr = ipv6_hdr(skb); @@ -188,6 +204,15 @@ int ipv6_rcv(struct sk_buff *skb, struct net_device *dev, struct packet_type *pt err: IP6_INC_STATS_BH(net, idev, IPSTATS_MIB_INHDRERRORS); drop: + DTRACE_IP(drop__in, + struct sk_buff * : pktinfo_t *, skb, + struct sock * : csinfo_t *, skb ? skb->sk : NULL, + void_ip_t * : ipinfo_t *, hdr, + struct net_device * : ifinfo_t *, skb ? skb->dev : NULL, + struct iphdr * : ipv4info_t *, NULL, + struct ipv6hdr * : ipv6info_t *, hdr, + char * : string, dropreason); + rcu_read_unlock(); kfree_skb(skb); return NET_RX_DROP; @@ -206,6 +231,8 @@ static int ip6_input_finish(struct sock *sk, struct sk_buff *skb) unsigned int nhoff; int nexthdr; bool raw; + struct ipv6hdr *hdr; + const char *dropreason = "header invalid"; /* * Parse extension headers @@ -238,12 +265,16 @@ resubmit: if (ipv6_addr_is_multicast(&hdr->daddr) && !ipv6_chk_mcast_addr(skb->dev, &hdr->daddr, &hdr->saddr) && - !ipv6_is_mld(skb, nexthdr, skb_network_header_len(skb))) + !ipv6_is_mld(skb, nexthdr, skb_network_header_len(skb))) { + dropreason = "destination is multicast"; goto discard; + } } if (!(ipprot->flags & INET6_PROTO_NOPOLICY) && - !xfrm6_policy_check(NULL, XFRM_POLICY_IN, skb)) + !xfrm6_policy_check(NULL, XFRM_POLICY_IN, skb)) { + dropreason = "policy failure"; goto discard; + } ret = ipprot->handler(skb); if (ret > 0) @@ -269,6 +300,17 @@ resubmit: discard: IP6_INC_STATS_BH(net, idev, IPSTATS_MIB_INDISCARDS); + + hdr = ipv6_hdr(skb); + DTRACE_IP(drop__in, + struct sk_buff * : pktinfo_t *, skb, + struct sock * : csinfo_t *, skb->sk, + void_ip_t * : ipinfo_t *, hdr, + struct net_device * : ifinfo_t *, skb->dev, + struct iphdr * : ipv4info_t *, NULL, + struct ipv6hdr * : ipv6info_t *, hdr, + char * : string, dropreason); + rcu_read_unlock(); kfree_skb(skb); return 0; @@ -277,6 +319,16 @@ discard: int ip6_input(struct sk_buff *skb) { + struct ipv6hdr *hdr = ipv6_hdr(skb); + + DTRACE_IP(receive, + struct sk_buff * : pktinfo_t *, skb, + struct sock * : csinfo_t *, skb->sk, + void_ip_t * : ipinfo_t *, hdr, + struct net_device * : ifinfo_t *, skb->dev, + struct iphdr * : ipv4info_t *, NULL, + struct ipv6hdr * : ipv6info_t *, hdr); + return NF_HOOK(NFPROTO_IPV6, NF_INET_LOCAL_IN, NULL, skb, skb->dev, NULL, ip6_input_finish); diff --git a/net/ipv6/ip6_output.c b/net/ipv6/ip6_output.c index 06bf4010d3ed..4abbd63e6f8d 100644 --- a/net/ipv6/ip6_output.c +++ b/net/ipv6/ip6_output.c @@ -55,6 +55,7 @@ #include #include #include +#include static int ip6_finish_output2(struct sock *sk, struct sk_buff *skb) { @@ -86,6 +87,16 @@ static int ip6_finish_output2(struct sock *sk, struct sk_buff *skb) dev_loopback_xmit); if (ipv6_hdr(skb)->hop_limit == 0) { + DTRACE_IP(drop__out, + struct sk_buff * : pktinfo_t *, skb, + struct sock * : csinfo_t *, skb->sk, + void_ip_t * : ipinfo_t *, ipv6_hdr(skb), + struct net_device * : ifinfo_t *, skb->dev, + struct iphdr * : ipv4info_t *, NULL, + struct ipv6hdr * : ipv6info_t *, + ipv6_hdr(skb), + char * : string, "hoplimit exceeded"); + IP6_INC_STATS(dev_net(dev), idev, IPSTATS_MIB_OUTDISCARDS); kfree_skb(skb); @@ -137,6 +148,15 @@ int ip6_output(struct sock *sk, struct sk_buff *skb) struct net_device *dev = skb_dst(skb)->dev; struct inet6_dev *idev = ip6_dst_idev(skb_dst(skb)); if (unlikely(idev->cnf.disable_ipv6)) { + DTRACE_IP(drop__out, + struct sk_buff * : pktinfo_t *, skb, + struct sock * : csinfo_t *, skb->sk, + void_ip_t * : ipinfo_t *, NULL, + struct net_device * : ifinfo_t *, skb->dev, + struct iphdr * : ipv4info_t *, NULL, + struct ipv6hdr * : ipv6info_t *, NULL, + char * : string, "IPv6 is disabled"); + IP6_INC_STATS(dev_net(dev), idev, IPSTATS_MIB_OUTDISCARDS); kfree_skb(skb); @@ -179,6 +199,15 @@ int ip6_xmit(struct sock *sk, struct sk_buff *skb, struct flowi6 *fl6, if (skb_headroom(skb) < head_room) { struct sk_buff *skb2 = skb_realloc_headroom(skb, head_room); if (!skb2) { + DTRACE_IP(drop__out, + struct sk_buff * : pktinfo_t *, skb, + struct sock * : csinfo_t *, skb->sk, + void_ip_t * : ipinfo_t *, NULL, + struct net_device * : ifinfo_t *, skb->dev, + struct iphdr * : ipv4info_t *, NULL, + struct ipv6hdr * : ipv6info_t *, NULL, + char * : string, "out of memory"); + IP6_INC_STATS(net, ip6_dst_idev(skb_dst(skb)), IPSTATS_MIB_OUTDISCARDS); kfree_skb(skb); @@ -224,6 +253,14 @@ int ip6_xmit(struct sock *sk, struct sk_buff *skb, struct flowi6 *fl6, if ((skb->len <= mtu) || skb->ignore_df || skb_is_gso(skb)) { IP6_UPD_PO_STATS(net, ip6_dst_idev(skb_dst(skb)), IPSTATS_MIB_OUT, skb->len); + DTRACE_IP(send, + struct sk_buff * : pktinfo_t *, skb, + struct sock * : csinfo_t *, skb->sk, + void_ip_t * : ipinfo_t *, hdr, + struct net_device * : ifinfo_t *, skb->dev, + struct iphdr *: ipv4info_t *, NULL, + struct ipv6hdr * : ipv6info_t *, hdr); + return NF_HOOK(NFPROTO_IPV6, NF_INET_LOCAL_OUT, sk, skb, NULL, dst->dev, dst_output_sk); } @@ -369,22 +406,32 @@ int ip6_forward(struct sk_buff *skb) struct inet6_skb_parm *opt = IP6CB(skb); struct net *net = dev_net(dst->dev); u32 mtu; + const char *dropreason; - if (net->ipv6.devconf_all->forwarding == 0) + if (net->ipv6.devconf_all->forwarding == 0) { + dropreason = "forwarding disabled"; goto error; + } - if (skb->pkt_type != PACKET_HOST) + if (skb->pkt_type != PACKET_HOST) { + dropreason = "non-host packet type cannot be forwarded"; goto drop; + } - if (unlikely(skb->sk)) + if (unlikely(skb->sk)) { + dropreason = "socket found for packet to be forwarded"; goto drop; + } - if (skb_warn_if_lro(skb)) + if (skb_warn_if_lro(skb)) { + dropreason = "LRO warning"; goto drop; + } if (!xfrm6_policy_check(NULL, XFRM_POLICY_FWD, skb)) { IP6_INC_STATS_BH(net, ip6_dst_idev(dst), IPSTATS_MIB_INDISCARDS); + dropreason = "forwarding disabled by policy"; goto drop; } @@ -414,6 +461,16 @@ int ip6_forward(struct sk_buff *skb) if (hdr->hop_limit <= 1) { /* Force OUTPUT device used as source address */ skb->dev = dst->dev; + + DTRACE_IP(drop__out, + struct sk_buff * : pktinfo_t *, skb, + struct sock * : csinfo_t *, skb->sk, + void_ip_t * : ipinfo_t *, hdr, + struct net_device * : ifinfo_t *, skb->dev, + struct iphdr * : ipv4info_t *, NULL, + struct ipv6hdr * : ipv6info_t *, hdr, + char * : string, "hoplimit exceeded"); + icmpv6_send(skb, ICMPV6_TIME_EXCEED, ICMPV6_EXC_HOPLIMIT, 0); IP6_INC_STATS_BH(net, ip6_dst_idev(dst), IPSTATS_MIB_INHDRERRORS); @@ -429,6 +486,8 @@ int ip6_forward(struct sk_buff *skb) if (proxied > 0) return ip6_input(skb); else if (proxied < 0) { + dropreason = "proxy router cannot forward"; + IP6_INC_STATS_BH(net, ip6_dst_idev(dst), IPSTATS_MIB_INDISCARDS); goto drop; @@ -438,6 +497,7 @@ int ip6_forward(struct sk_buff *skb) if (!xfrm6_route_forward(skb)) { IP6_INC_STATS_BH(net, ip6_dst_idev(dst), IPSTATS_MIB_INDISCARDS); + dropreason = "forwarding disabled for destination"; goto drop; } dst = skb_dst(skb); @@ -476,9 +536,12 @@ int ip6_forward(struct sk_buff *skb) /* This check is security critical. */ if (addrtype == IPV6_ADDR_ANY || - addrtype & (IPV6_ADDR_MULTICAST | IPV6_ADDR_LOOPBACK)) + addrtype & (IPV6_ADDR_MULTICAST | IPV6_ADDR_LOOPBACK)) { + dropreason = "invalid address type for forwarding"; goto error; + } if (addrtype & IPV6_ADDR_LINKLOCAL) { + dropreason = "invalid address type for forwarding"; icmpv6_send(skb, ICMPV6_DEST_UNREACH, ICMPV6_NOT_NEIGHBOUR, 0); goto error; @@ -492,6 +555,15 @@ int ip6_forward(struct sk_buff *skb) if (ip6_pkt_too_big(skb, mtu)) { /* Again, force OUTPUT device used as source address */ skb->dev = dst->dev; + DTRACE_IP(drop__out, + struct sk_buff * : pktinfo_t *, skb, + struct sock * : csinfo_t *, skb->sk, + void_ip_t * : ipinfo_t *, hdr, + struct net_device * : ifinfo_t *, skb->dev, + struct iphdr * : ipv4info_t *, NULL, + struct ipv6hdr * : ipv6info_t *, hdr, + char * : string, "packet too big"); + icmpv6_send(skb, ICMPV6_PKT_TOOBIG, 0, mtu); IP6_INC_STATS_BH(net, ip6_dst_idev(dst), IPSTATS_MIB_INTOOBIGERRORS); @@ -504,6 +576,7 @@ int ip6_forward(struct sk_buff *skb) if (skb_cow(skb, dst->dev->hard_header_len)) { IP6_INC_STATS_BH(net, ip6_dst_idev(dst), IPSTATS_MIB_OUTDISCARDS); + dropreason = "copy-on-write failed"; goto drop; } @@ -522,6 +595,15 @@ int ip6_forward(struct sk_buff *skb) error: IP6_INC_STATS_BH(net, ip6_dst_idev(dst), IPSTATS_MIB_INADDRERRORS); drop: + DTRACE_IP(drop__out, + struct sk_buff * : pktinfo_t *, skb, + struct sock * : csinfo_t *, skb->sk, + void_ip_t * : ipinfo_t *, hdr, + struct net_device * : ifinfo_t *, skb->dev, + struct iphdr * : ipv4info_t *, NULL, + struct ipv6hdr * : ipv6info_t *, hdr, + char * : string, dropreason); + kfree_skb(skb); return -EINVAL; } @@ -1249,6 +1331,7 @@ static int __ip6_append_data(struct sock *sk, struct rt6_info *rt = (struct rt6_info *)cork->dst; struct ipv6_txoptions *opt = v6_cork->opt; int csummode = CHECKSUM_NONE; + const char *dropreason; skb = skb_peek_tail(queue); if (!skb) { @@ -1286,6 +1369,7 @@ static int __ip6_append_data(struct sock *sk, sk->sk_protocol == IPPROTO_RAW)) { ipv6_local_rxpmtu(sk, fl6, mtu - headersize + sizeof(struct ipv6hdr)); + dropreason = "fragmentation needed but disabled"; goto emsgsize; } @@ -1294,6 +1378,15 @@ emsgsize: ipv6_local_error(sk, EMSGSIZE, fl6, mtu - headersize + sizeof(struct ipv6hdr)); + DTRACE_IP(drop__out, + struct sk_buff * : pktinfo_t *, skb, + struct sock * : csinfo_t *, skb->sk, + void_ip_t * : ipinfo_t *, NULL, + struct net_device * : ifinfo_t *, skb->dev, + struct iphdr * : ipv4info_t *, NULL, + struct ipv6hdr * : ipv6info_t *, NULL, + char * : string, "packet too big"); + return -EMSGSIZE; } } @@ -1340,8 +1433,10 @@ emsgsize: err = ip6_ufo_append_data(sk, queue, getfrag, from, length, hh_len, fragheaderlen, transhdrlen, mtu, flags, rt); - if (err) + if (err) { + dropreason = "could not append data"; goto error; + } return 0; } @@ -1422,8 +1517,11 @@ alloc_new_skb: if (unlikely(!skb)) err = -ENOBUFS; } - if (!skb) + if (!skb) { + dropreason = "out of memory"; goto error; + } + /* * Fill in the control structures */ @@ -1462,10 +1560,14 @@ alloc_new_skb: if (copy < 0) { err = -EINVAL; kfree_skb(skb); + skb = NULL; + dropreason = "invalid header"; goto error; } else if (copy > 0 && getfrag(from, data + transhdrlen, offset, copy, fraggap, skb) < 0) { err = -EFAULT; + dropreason = "could not get fragment"; kfree_skb(skb); + skb = NULL; goto error; } @@ -1493,20 +1595,25 @@ alloc_new_skb: offset, copy, off, skb) < 0) { __skb_trim(skb, off); err = -EFAULT; + dropreason = "could not get fragment"; goto error; } } else { int i = skb_shinfo(skb)->nr_frags; err = -ENOMEM; - if (!sk_page_frag_refill(sk, pfrag)) + if (!sk_page_frag_refill(sk, pfrag)) { + dropreason = "out of memory"; goto error; + } if (!skb_can_coalesce(skb, i, pfrag->page, pfrag->offset)) { err = -EMSGSIZE; - if (i == MAX_SKB_FRAGS) + if (i == MAX_SKB_FRAGS) { + dropreason = "too many fragments"; goto error; + } __skb_fill_page_desc(skb, i, pfrag->page, pfrag->offset, 0); @@ -1516,8 +1623,10 @@ alloc_new_skb: copy = min_t(int, copy, pfrag->size - pfrag->offset); if (getfrag(from, page_address(pfrag->page) + pfrag->offset, - offset, copy, skb->len, skb) < 0) + offset, copy, skb->len, skb) < 0) { + dropreason = "could not get fragment"; goto error_efault; + } pfrag->offset += copy; skb_frag_size_add(&skb_shinfo(skb)->frags[i - 1], copy); @@ -1535,6 +1644,15 @@ alloc_new_skb: error_efault: err = -EFAULT; error: + DTRACE_IP(drop__out, + struct sk_buff * : pktinfo_t *, skb, + struct sock * : csinfo_t *, skb ? skb->sk : NULL, + void_ip_t * : ipinfo_t *, NULL, + struct net_device * : ifinfo_t *, skb ? skb->dev : NULL, + struct iphdr * : ipv4info_t *, NULL, + struct ipv6hdr * : ipv6info_t *, NULL, + char * : string, dropreason); + cork->length -= length; IP6_INC_STATS(sock_net(sk), rt->rt6i_idev, IPSTATS_MIB_OUTDISCARDS); return err; @@ -1681,9 +1799,19 @@ int ip6_send_skb(struct sk_buff *skb) if (err) { if (err > 0) err = net_xmit_errno(err); - if (err) + if (err) { + DTRACE_IP(drop__out, + struct sk_buff * : pktinfo_t *, skb, + struct sock * : csinfo_t *, skb->sk, + void_ip_t * : ipinfo_t *, ipv6_hdr(skb), + struct net_device * : ifinfo_t *, skb->dev, + struct iphdr * : ipv4info_t *, NULL, + struct ipv6hdr * : ipv6info_t *, ipv6_hdr(skb), + char * : string, "out of memory"); + IP6_INC_STATS(net, rt->rt6i_idev, IPSTATS_MIB_OUTDISCARDS); + } } return err; @@ -1709,9 +1837,19 @@ static void __ip6_flush_pending_frames(struct sock *sk, struct sk_buff *skb; while ((skb = __skb_dequeue_tail(queue)) != NULL) { - if (skb_dst(skb)) + if (skb_dst(skb)) { + DTRACE_IP(drop__out, + struct sk_buff * : pktinfo_t *, skb, + struct sock * : csinfo_t *, skb->sk, + void_ip_t * : ipinfo_t *, ipv6_hdr(skb), + struct net_device * : ifinfo_t *, skb->dev, + struct iphdr * : ipv4info_t *, NULL, + struct ipv6hdr * : ipv6info_t *, + ipv6_hdr(skb), + char * : string, "flushing pending frames"); IP6_INC_STATS(sock_net(sk), ip6_dst_idev(skb_dst(skb)), IPSTATS_MIB_OUTDISCARDS); + } kfree_skb(skb); } diff --git a/net/ipv6/mcast.c b/net/ipv6/mcast.c index 083b2927fc67..63397220b675 100644 --- a/net/ipv6/mcast.c +++ b/net/ipv6/mcast.c @@ -64,6 +64,8 @@ #include +#include + /* Ensure that we have struct in6_addr aligned on 32bit word. */ static void *__mld2_query_bugs[] __attribute__((__unused__)) = { BUILD_BUG_ON_NULL(offsetof(struct mld2_query, mld2q_srcs) % 4), @@ -1639,11 +1641,22 @@ static void mld_sendpack(struct sk_buff *skb) dst = NULL; } skb_dst_set(skb, dst); - if (err) - goto err_out; + if (err) { + kfree_skb(skb); + skb = NULL; + goto out; + } payload_len = skb->len; + DTRACE_IP(send, + struct sk_buff * : pktinfo_t *, skb, + struct sock * : csinfo_t *, skb->sk, + void_ip_t * : ipinfo_t *, ipv6_hdr(skb), + struct net_device * : ifinfo_t *, skb->dev, + struct iphdr *: ipv4info_t *, NULL, + struct ipv6hdr * : ipv6info_t *, ipv6_hdr(skb)); + err = NF_HOOK(NFPROTO_IPV6, NF_INET_LOCAL_OUT, net->ipv6.igmp_sk, skb, NULL, skb->dev, dst_output_sk); @@ -1653,15 +1666,20 @@ out: ICMP6_INC_STATS(net, idev, ICMP6_MIB_OUTMSGS); IP6_UPD_PO_STATS(net, idev, IPSTATS_MIB_OUTMCAST, payload_len); } else { + DTRACE_IP(drop__out, + struct sk_buff * : pktinfo_t *, skb, + struct sock * : csinfo_t *, skb ? skb->sk : NULL, + void_ip_t * : ipinfo_t *, skb ? ipv6_hdr(skb) : NULL, + struct net_device * : ifinfo_t *, skb ? skb->dev : NULL, + struct iphdr * : ipv4info_t *, NULL, + struct ipv6hdr * : ipv6info_t *, skb ? ipv6_hdr(skb) : NULL, + char * : string, "multicast send error"); + IP6_INC_STATS(net, idev, IPSTATS_MIB_OUTDISCARDS); } rcu_read_unlock(); return; - -err_out: - kfree_skb(skb); - goto out; } static int grec_size(struct ifmcaddr6 *pmc, int type, int gdel, int sdel) @@ -1965,6 +1983,15 @@ static void igmp6_send(struct in6_addr *addr, struct net_device *dev, int type) if (!skb) { rcu_read_lock(); + DTRACE_IP(drop__out, + struct sk_buff * : pktinfo_t *, NULL, + struct sock * : csinfo_t *, sk, + void_ip_t * : ipinfo_t *, NULL, + struct net_device * : ifinfo_t *, NULL, + struct iphdr * : ipv4info_t *, NULL, + struct ipv6hdr * : ipv6info_t *, NULL, + char * : string, "out of memory"); + IP6_INC_STATS(net, __in6_dev_get(dev), IPSTATS_MIB_OUTDISCARDS); rcu_read_unlock(); @@ -2004,10 +2031,21 @@ static void igmp6_send(struct in6_addr *addr, struct net_device *dev, int type) dst = icmp6_dst_alloc(skb->dev, &fl6); if (IS_ERR(dst)) { err = PTR_ERR(dst); - goto err_out; + kfree_skb(skb); + skb = NULL; + goto out; } skb_dst_set(skb, dst); + + DTRACE_IP(send, + struct sk_buff * : pktinfo_t *, skb, + struct sock * : csinfo_t *, skb->sk, + void_ip_t * : ipinfo_t *, ipv6_hdr(skb), + struct net_device * : ifinfo_t *, skb->dev, + struct iphdr *: ipv4info_t *, NULL, + struct ipv6hdr * : ipv6info_t *, ipv6_hdr(skb)); + err = NF_HOOK(NFPROTO_IPV6, NF_INET_LOCAL_OUT, sk, skb, NULL, skb->dev, dst_output_sk); out: @@ -2015,15 +2053,21 @@ out: ICMP6MSGOUT_INC_STATS(net, idev, type); ICMP6_INC_STATS(net, idev, ICMP6_MIB_OUTMSGS); IP6_UPD_PO_STATS(net, idev, IPSTATS_MIB_OUTMCAST, full_len); - } else + } else { + DTRACE_IP(drop__out, + struct sk_buff * : pktinfo_t *, skb, + struct sock * : csinfo_t *, skb ? skb->sk : NULL, + void_ip_t * : ipinfo_t *, skb ? ipv6_hdr(skb) : NULL, + struct net_device * : ifinfo_t *, skb ? skb->dev : NULL, + struct iphdr * : ipv4info_t *, NULL, + struct ipv6hdr * : ipv6info_t *, skb ? ipv6_hdr(skb) : NULL, + char * : string, "multicast send error"); + IP6_INC_STATS(net, idev, IPSTATS_MIB_OUTDISCARDS); + } rcu_read_unlock(); return; - -err_out: - kfree_skb(skb); - goto out; } static void mld_send_initial_cr(struct inet6_dev *idev) diff --git a/net/ipv6/ndisc.c b/net/ipv6/ndisc.c index 96f153c0846b..1ebb751b9ff5 100644 --- a/net/ipv6/ndisc.c +++ b/net/ipv6/ndisc.c @@ -71,6 +71,7 @@ #include #include +#include /* Set to 3 to get tracing... */ #define ND_DEBUG 1 @@ -463,6 +464,14 @@ static void ndisc_send_skb(struct sk_buff *skb, idev = __in6_dev_get(dst->dev); IP6_UPD_PO_STATS(net, idev, IPSTATS_MIB_OUT, skb->len); + DTRACE_IP(send, + struct sk_buff * : pktinfo_t *, skb, + struct sock * : csinfo_t *, skb->sk, + void_ip_t * : ipinfo_t *, ipv6_hdr(skb), + struct net_device * : ifinfo_t *, skb->dev, + struct iphdr * : ipv4info_t *, NULL, + struct ipv6hdr * : ipv6info_t *, ipv6_hdr(skb)); + err = NF_HOOK(NFPROTO_IPV6, NF_INET_LOCAL_OUT, sk, skb, NULL, dst->dev, dst_output_sk); diff --git a/net/ipv6/output_core.c b/net/ipv6/output_core.c index 85892af57364..5c04e5213cab 100644 --- a/net/ipv6/output_core.c +++ b/net/ipv6/output_core.c @@ -8,6 +8,7 @@ #include #include #include +#include static u32 __ipv6_select_ident(struct net *net, u32 hashrnd, struct in6_addr *dst, struct in6_addr *src) @@ -146,6 +147,14 @@ static int __ip6_local_out_sk(struct sock *sk, struct sk_buff *skb) ipv6_hdr(skb)->payload_len = htons(len); IP6CB(skb)->nhoff = offsetof(struct ipv6hdr, nexthdr); + DTRACE_IP(send, + struct sk_buff * : pktinfo_t *, skb, + struct sock * : csinfo_t *, skb->sk, + void_ip_t * : ipinfo_t *, ipv6_hdr(skb), + struct net_device * : ifinfo_t *, skb->dev, + struct iphdr *: ipv4info_t *, NULL, + struct ipv6hdr * : ipv6info_t *, ipv6_hdr(skb)); + return nf_hook(NFPROTO_IPV6, NF_INET_LOCAL_OUT, sk, skb, NULL, skb_dst(skb)->dev, dst_output_sk); } diff --git a/net/ipv6/raw.c b/net/ipv6/raw.c index 2c639aee12cb..ab8fabfecee5 100644 --- a/net/ipv6/raw.c +++ b/net/ipv6/raw.c @@ -62,6 +62,7 @@ #include #include #include +#include #define ICMPV6_HDRLEN 4 /* ICMPv6 header, RFC 4443 Section 2.1 */ @@ -652,6 +653,14 @@ static int rawv6_send_hdrinc(struct sock *sk, struct msghdr *msg, int length, goto error_fault; IP6_UPD_PO_STATS(sock_net(sk), rt->rt6i_idev, IPSTATS_MIB_OUT, skb->len); + DTRACE_IP(send, + struct sk_buff * : pktinfo_t *, skb, + struct sock * : csinfo_t *, skb->sk, + void_ip_t * : ipinfo_t *, ipv6_hdr(skb), + struct net_device * : ifinfo_t *, skb->dev, + struct iphdr * : ipv4info_t *, NULL, + struct ipv6hdr * : ipv6info_t *, ipv6_hdr(skb)); + err = NF_HOOK(NFPROTO_IPV6, NF_INET_LOCAL_OUT, sk, skb, NULL, rt->dst.dev, dst_output_sk); if (err > 0) @@ -664,7 +673,16 @@ out: error_fault: err = -EFAULT; kfree_skb(skb); + skb = NULL; error: + DTRACE_IP(send, + struct sk_buff * : pktinfo_t *, skb, + struct sock * : csinfo_t *, skb ? skb->sk : NULL, + void_ip_t * : ipinfo_t *, skb ? ipv6_hdr(skb) : NULL, + struct net_device * : ifinfo_t *, skb ? skb->dev : NULL, + struct iphdr * : ipv4info_t *, NULL, + struct ipv6hdr * : ipv6info_t *, skb ? ipv6_hdr(skb) : NULL); + IP6_INC_STATS(sock_net(sk), rt->rt6i_idev, IPSTATS_MIB_OUTDISCARDS); if (err == -ENOBUFS && !np->recverr) err = 0;