1c8d7b98bSPablo Neira Ayuso /* (C) 1999-2001 Paul `Rusty' Russell 2c8d7b98bSPablo Neira Ayuso * (C) 2002-2004 Netfilter Core Team <coreteam@netfilter.org> 3c8d7b98bSPablo Neira Ayuso * 4c8d7b98bSPablo Neira Ayuso * This program is free software; you can redistribute it and/or modify 5c8d7b98bSPablo Neira Ayuso * it under the terms of the GNU General Public License version 2 as 6c8d7b98bSPablo Neira Ayuso * published by the Free Software Foundation. 7c8d7b98bSPablo Neira Ayuso */ 8c8d7b98bSPablo Neira Ayuso 9ab2d7251SPablo Neira Ayuso #include <linux/module.h> 10c8d7b98bSPablo Neira Ayuso #include <net/ip.h> 11c8d7b98bSPablo Neira Ayuso #include <net/tcp.h> 12c8d7b98bSPablo Neira Ayuso #include <net/route.h> 13c8d7b98bSPablo Neira Ayuso #include <net/dst.h> 1456768644SFlorian Westphal #include <net/netfilter/ipv4/nf_reject.h> 15c8d7b98bSPablo Neira Ayuso #include <linux/netfilter_ipv4.h> 16c737b7c4SFlorian Westphal #include <linux/netfilter_bridge.h> 17c8d7b98bSPablo Neira Ayuso 18052b9498SPablo Neira Ayuso const struct tcphdr *nf_reject_ip_tcphdr_get(struct sk_buff *oldskb, 19052b9498SPablo Neira Ayuso struct tcphdr *_oth, int hook) 20c8d7b98bSPablo Neira Ayuso { 21c8d7b98bSPablo Neira Ayuso const struct tcphdr *oth; 22c8d7b98bSPablo Neira Ayuso 23c8d7b98bSPablo Neira Ayuso /* IP header checks: fragment. */ 24c8d7b98bSPablo Neira Ayuso if (ip_hdr(oldskb)->frag_off & htons(IP_OFFSET)) 25052b9498SPablo Neira Ayuso return NULL; 26c8d7b98bSPablo Neira Ayuso 27e1dbbc59SLiping Zhang if (ip_hdr(oldskb)->protocol != IPPROTO_TCP) 28e1dbbc59SLiping Zhang return NULL; 29e1dbbc59SLiping Zhang 30c8d7b98bSPablo Neira Ayuso oth = skb_header_pointer(oldskb, ip_hdrlen(oldskb), 31052b9498SPablo Neira Ayuso sizeof(struct tcphdr), _oth); 32c8d7b98bSPablo Neira Ayuso if (oth == NULL) 33052b9498SPablo Neira Ayuso return NULL; 34c8d7b98bSPablo Neira Ayuso 35c8d7b98bSPablo Neira Ayuso /* No RST for RST. */ 36c8d7b98bSPablo Neira Ayuso if (oth->rst) 37052b9498SPablo Neira Ayuso return NULL; 38c8d7b98bSPablo Neira Ayuso 39c8d7b98bSPablo Neira Ayuso /* Check checksum */ 40c8d7b98bSPablo Neira Ayuso if (nf_ip_checksum(oldskb, hook, ip_hdrlen(oldskb), IPPROTO_TCP)) 41052b9498SPablo Neira Ayuso return NULL; 42c8d7b98bSPablo Neira Ayuso 43052b9498SPablo Neira Ayuso return oth; 44052b9498SPablo Neira Ayuso } 45052b9498SPablo Neira Ayuso EXPORT_SYMBOL_GPL(nf_reject_ip_tcphdr_get); 46c8d7b98bSPablo Neira Ayuso 47052b9498SPablo Neira Ayuso struct iphdr *nf_reject_iphdr_put(struct sk_buff *nskb, 48052b9498SPablo Neira Ayuso const struct sk_buff *oldskb, 49a03a8dbeSFlorian Westphal __u8 protocol, int ttl) 50052b9498SPablo Neira Ayuso { 51052b9498SPablo Neira Ayuso struct iphdr *niph, *oiph = ip_hdr(oldskb); 52c8d7b98bSPablo Neira Ayuso 53c8d7b98bSPablo Neira Ayuso skb_reset_network_header(nskb); 54c8d7b98bSPablo Neira Ayuso niph = (struct iphdr *)skb_put(nskb, sizeof(struct iphdr)); 55c8d7b98bSPablo Neira Ayuso niph->version = 4; 56c8d7b98bSPablo Neira Ayuso niph->ihl = sizeof(struct iphdr) / 4; 57c8d7b98bSPablo Neira Ayuso niph->tos = 0; 58c8d7b98bSPablo Neira Ayuso niph->id = 0; 59c8d7b98bSPablo Neira Ayuso niph->frag_off = htons(IP_DF); 60052b9498SPablo Neira Ayuso niph->protocol = protocol; 61c8d7b98bSPablo Neira Ayuso niph->check = 0; 62c8d7b98bSPablo Neira Ayuso niph->saddr = oiph->daddr; 63c8d7b98bSPablo Neira Ayuso niph->daddr = oiph->saddr; 64052b9498SPablo Neira Ayuso niph->ttl = ttl; 65052b9498SPablo Neira Ayuso 66052b9498SPablo Neira Ayuso nskb->protocol = htons(ETH_P_IP); 67052b9498SPablo Neira Ayuso 68052b9498SPablo Neira Ayuso return niph; 69052b9498SPablo Neira Ayuso } 70052b9498SPablo Neira Ayuso EXPORT_SYMBOL_GPL(nf_reject_iphdr_put); 71052b9498SPablo Neira Ayuso 72052b9498SPablo Neira Ayuso void nf_reject_ip_tcphdr_put(struct sk_buff *nskb, const struct sk_buff *oldskb, 73052b9498SPablo Neira Ayuso const struct tcphdr *oth) 74052b9498SPablo Neira Ayuso { 75052b9498SPablo Neira Ayuso struct iphdr *niph = ip_hdr(nskb); 76052b9498SPablo Neira Ayuso struct tcphdr *tcph; 77c8d7b98bSPablo Neira Ayuso 78c8d7b98bSPablo Neira Ayuso skb_reset_transport_header(nskb); 79c8d7b98bSPablo Neira Ayuso tcph = (struct tcphdr *)skb_put(nskb, sizeof(struct tcphdr)); 80c8d7b98bSPablo Neira Ayuso memset(tcph, 0, sizeof(*tcph)); 81c8d7b98bSPablo Neira Ayuso tcph->source = oth->dest; 82c8d7b98bSPablo Neira Ayuso tcph->dest = oth->source; 83c8d7b98bSPablo Neira Ayuso tcph->doff = sizeof(struct tcphdr) / 4; 84c8d7b98bSPablo Neira Ayuso 85052b9498SPablo Neira Ayuso if (oth->ack) { 86c8d7b98bSPablo Neira Ayuso tcph->seq = oth->ack_seq; 87052b9498SPablo Neira Ayuso } else { 88c8d7b98bSPablo Neira Ayuso tcph->ack_seq = htonl(ntohl(oth->seq) + oth->syn + oth->fin + 89c8d7b98bSPablo Neira Ayuso oldskb->len - ip_hdrlen(oldskb) - 90c8d7b98bSPablo Neira Ayuso (oth->doff << 2)); 91c8d7b98bSPablo Neira Ayuso tcph->ack = 1; 92c8d7b98bSPablo Neira Ayuso } 93c8d7b98bSPablo Neira Ayuso 94c8d7b98bSPablo Neira Ayuso tcph->rst = 1; 95c8d7b98bSPablo Neira Ayuso tcph->check = ~tcp_v4_check(sizeof(struct tcphdr), niph->saddr, 96c8d7b98bSPablo Neira Ayuso niph->daddr, 0); 97c8d7b98bSPablo Neira Ayuso nskb->ip_summed = CHECKSUM_PARTIAL; 98c8d7b98bSPablo Neira Ayuso nskb->csum_start = (unsigned char *)tcph - nskb->head; 99c8d7b98bSPablo Neira Ayuso nskb->csum_offset = offsetof(struct tcphdr, check); 100052b9498SPablo Neira Ayuso } 101052b9498SPablo Neira Ayuso EXPORT_SYMBOL_GPL(nf_reject_ip_tcphdr_put); 102052b9498SPablo Neira Ayuso 103052b9498SPablo Neira Ayuso /* Send RST reply */ 104372892ecSEric W. Biederman void nf_send_reset(struct net *net, struct sk_buff *oldskb, int hook) 105052b9498SPablo Neira Ayuso { 106052b9498SPablo Neira Ayuso struct sk_buff *nskb; 107052b9498SPablo Neira Ayuso const struct iphdr *oiph; 108052b9498SPablo Neira Ayuso struct iphdr *niph; 109052b9498SPablo Neira Ayuso const struct tcphdr *oth; 110052b9498SPablo Neira Ayuso struct tcphdr _oth; 111052b9498SPablo Neira Ayuso 112052b9498SPablo Neira Ayuso oth = nf_reject_ip_tcphdr_get(oldskb, &_oth, hook); 113052b9498SPablo Neira Ayuso if (!oth) 114052b9498SPablo Neira Ayuso return; 115052b9498SPablo Neira Ayuso 116052b9498SPablo Neira Ayuso if (skb_rtable(oldskb)->rt_flags & (RTCF_BROADCAST | RTCF_MULTICAST)) 117052b9498SPablo Neira Ayuso return; 118052b9498SPablo Neira Ayuso 119052b9498SPablo Neira Ayuso oiph = ip_hdr(oldskb); 120052b9498SPablo Neira Ayuso 121052b9498SPablo Neira Ayuso nskb = alloc_skb(sizeof(struct iphdr) + sizeof(struct tcphdr) + 122052b9498SPablo Neira Ayuso LL_MAX_HEADER, GFP_ATOMIC); 123052b9498SPablo Neira Ayuso if (!nskb) 124052b9498SPablo Neira Ayuso return; 125c8d7b98bSPablo Neira Ayuso 126c8d7b98bSPablo Neira Ayuso /* ip_route_me_harder expects skb->dst to be set */ 127c8d7b98bSPablo Neira Ayuso skb_dst_set_noref(nskb, skb_dst(oldskb)); 128c8d7b98bSPablo Neira Ayuso 129052b9498SPablo Neira Ayuso skb_reserve(nskb, LL_MAX_HEADER); 130052b9498SPablo Neira Ayuso niph = nf_reject_iphdr_put(nskb, oldskb, IPPROTO_TCP, 131052b9498SPablo Neira Ayuso ip4_dst_hoplimit(skb_dst(nskb))); 132052b9498SPablo Neira Ayuso nf_reject_ip_tcphdr_put(nskb, oldskb, oth); 133052b9498SPablo Neira Ayuso 134e45f5066SEric W. Biederman if (ip_route_me_harder(net, nskb, RTN_UNSPEC)) 135c8d7b98bSPablo Neira Ayuso goto free_nskb; 136c8d7b98bSPablo Neira Ayuso 137c8d7b98bSPablo Neira Ayuso /* "Never happens" */ 138c8d7b98bSPablo Neira Ayuso if (nskb->len > dst_mtu(skb_dst(nskb))) 139c8d7b98bSPablo Neira Ayuso goto free_nskb; 140c8d7b98bSPablo Neira Ayuso 141c8d7b98bSPablo Neira Ayuso nf_ct_attach(nskb, oldskb); 142c8d7b98bSPablo Neira Ayuso 143c8d7b98bSPablo Neira Ayuso #if IS_ENABLED(CONFIG_BRIDGE_NETFILTER) 144c8d7b98bSPablo Neira Ayuso /* If we use ip_local_out for bridged traffic, the MAC source on 145c8d7b98bSPablo Neira Ayuso * the RST will be ours, instead of the destination's. This confuses 146c8d7b98bSPablo Neira Ayuso * some routers/firewalls, and they drop the packet. So we need to 147c8d7b98bSPablo Neira Ayuso * build the eth header using the original destination's MAC as the 148c8d7b98bSPablo Neira Ayuso * source, and send the RST packet directly. 149c8d7b98bSPablo Neira Ayuso */ 150c8d7b98bSPablo Neira Ayuso if (oldskb->nf_bridge) { 151c8d7b98bSPablo Neira Ayuso struct ethhdr *oeth = eth_hdr(oldskb); 152c737b7c4SFlorian Westphal 153c737b7c4SFlorian Westphal nskb->dev = nf_bridge_get_physindev(oldskb); 154c8d7b98bSPablo Neira Ayuso niph->tot_len = htons(nskb->len); 155c8d7b98bSPablo Neira Ayuso ip_send_check(niph); 156c8d7b98bSPablo Neira Ayuso if (dev_hard_header(nskb, nskb->dev, ntohs(nskb->protocol), 157c8d7b98bSPablo Neira Ayuso oeth->h_source, oeth->h_dest, nskb->len) < 0) 158c8d7b98bSPablo Neira Ayuso goto free_nskb; 159c8d7b98bSPablo Neira Ayuso dev_queue_xmit(nskb); 160c8d7b98bSPablo Neira Ayuso } else 161c8d7b98bSPablo Neira Ayuso #endif 16233224b16SEric W. Biederman ip_local_out(net, nskb->sk, nskb); 163c8d7b98bSPablo Neira Ayuso 164c8d7b98bSPablo Neira Ayuso return; 165c8d7b98bSPablo Neira Ayuso 166c8d7b98bSPablo Neira Ayuso free_nskb: 167c8d7b98bSPablo Neira Ayuso kfree_skb(nskb); 168c8d7b98bSPablo Neira Ayuso } 169c8d7b98bSPablo Neira Ayuso EXPORT_SYMBOL_GPL(nf_send_reset); 170ab2d7251SPablo Neira Ayuso 171ee586bbcSFlorian Westphal void nf_send_unreach(struct sk_buff *skb_in, int code, int hook) 172ee586bbcSFlorian Westphal { 173ee586bbcSFlorian Westphal struct iphdr *iph = ip_hdr(skb_in); 174ee586bbcSFlorian Westphal u8 proto; 175ee586bbcSFlorian Westphal 176ee586bbcSFlorian Westphal if (skb_in->csum_bad || iph->frag_off & htons(IP_OFFSET)) 177ee586bbcSFlorian Westphal return; 178ee586bbcSFlorian Westphal 179ee586bbcSFlorian Westphal if (skb_csum_unnecessary(skb_in)) { 180ee586bbcSFlorian Westphal icmp_send(skb_in, ICMP_DEST_UNREACH, code, 0); 181ee586bbcSFlorian Westphal return; 182ee586bbcSFlorian Westphal } 183ee586bbcSFlorian Westphal 184ee586bbcSFlorian Westphal if (iph->protocol == IPPROTO_TCP || iph->protocol == IPPROTO_UDP) 185ee586bbcSFlorian Westphal proto = iph->protocol; 186ee586bbcSFlorian Westphal else 187ee586bbcSFlorian Westphal proto = 0; 188ee586bbcSFlorian Westphal 189ee586bbcSFlorian Westphal if (nf_ip_checksum(skb_in, hook, ip_hdrlen(skb_in), proto) == 0) 190ee586bbcSFlorian Westphal icmp_send(skb_in, ICMP_DEST_UNREACH, code, 0); 191ee586bbcSFlorian Westphal } 192ee586bbcSFlorian Westphal EXPORT_SYMBOL_GPL(nf_send_unreach); 193ee586bbcSFlorian Westphal 194ab2d7251SPablo Neira Ayuso MODULE_LICENSE("GPL"); 195