1c8d7b98bec43faaa6583c3135030be5eb4693acbPablo Neira Ayuso/* (C) 1999-2001 Paul `Rusty' Russell 2c8d7b98bec43faaa6583c3135030be5eb4693acbPablo Neira Ayuso * (C) 2002-2004 Netfilter Core Team <coreteam@netfilter.org> 3c8d7b98bec43faaa6583c3135030be5eb4693acbPablo Neira Ayuso * 4c8d7b98bec43faaa6583c3135030be5eb4693acbPablo Neira Ayuso * This program is free software; you can redistribute it and/or modify 5c8d7b98bec43faaa6583c3135030be5eb4693acbPablo Neira Ayuso * it under the terms of the GNU General Public License version 2 as 6c8d7b98bec43faaa6583c3135030be5eb4693acbPablo Neira Ayuso * published by the Free Software Foundation. 7c8d7b98bec43faaa6583c3135030be5eb4693acbPablo Neira Ayuso */ 8c8d7b98bec43faaa6583c3135030be5eb4693acbPablo Neira Ayuso 9ab2d7251d666995740da17b2a51ca545ac5dd037Pablo Neira Ayuso#include <linux/module.h> 10c8d7b98bec43faaa6583c3135030be5eb4693acbPablo Neira Ayuso#include <net/ip.h> 11c8d7b98bec43faaa6583c3135030be5eb4693acbPablo Neira Ayuso#include <net/tcp.h> 12c8d7b98bec43faaa6583c3135030be5eb4693acbPablo Neira Ayuso#include <net/route.h> 13c8d7b98bec43faaa6583c3135030be5eb4693acbPablo Neira Ayuso#include <net/dst.h> 14c8d7b98bec43faaa6583c3135030be5eb4693acbPablo Neira Ayuso#include <linux/netfilter_ipv4.h> 15052b9498eea532deb5de75277a53f6e0623215dcPablo Neira Ayuso#include <net/netfilter/ipv4/nf_reject.h> 16c8d7b98bec43faaa6583c3135030be5eb4693acbPablo Neira Ayuso 17052b9498eea532deb5de75277a53f6e0623215dcPablo Neira Ayusoconst struct tcphdr *nf_reject_ip_tcphdr_get(struct sk_buff *oldskb, 18052b9498eea532deb5de75277a53f6e0623215dcPablo Neira Ayuso struct tcphdr *_oth, int hook) 19c8d7b98bec43faaa6583c3135030be5eb4693acbPablo Neira Ayuso{ 20c8d7b98bec43faaa6583c3135030be5eb4693acbPablo Neira Ayuso const struct tcphdr *oth; 21c8d7b98bec43faaa6583c3135030be5eb4693acbPablo Neira Ayuso 22c8d7b98bec43faaa6583c3135030be5eb4693acbPablo Neira Ayuso /* IP header checks: fragment. */ 23c8d7b98bec43faaa6583c3135030be5eb4693acbPablo Neira Ayuso if (ip_hdr(oldskb)->frag_off & htons(IP_OFFSET)) 24052b9498eea532deb5de75277a53f6e0623215dcPablo Neira Ayuso return NULL; 25c8d7b98bec43faaa6583c3135030be5eb4693acbPablo Neira Ayuso 26c8d7b98bec43faaa6583c3135030be5eb4693acbPablo Neira Ayuso oth = skb_header_pointer(oldskb, ip_hdrlen(oldskb), 27052b9498eea532deb5de75277a53f6e0623215dcPablo Neira Ayuso sizeof(struct tcphdr), _oth); 28c8d7b98bec43faaa6583c3135030be5eb4693acbPablo Neira Ayuso if (oth == NULL) 29052b9498eea532deb5de75277a53f6e0623215dcPablo Neira Ayuso return NULL; 30c8d7b98bec43faaa6583c3135030be5eb4693acbPablo Neira Ayuso 31c8d7b98bec43faaa6583c3135030be5eb4693acbPablo Neira Ayuso /* No RST for RST. */ 32c8d7b98bec43faaa6583c3135030be5eb4693acbPablo Neira Ayuso if (oth->rst) 33052b9498eea532deb5de75277a53f6e0623215dcPablo Neira Ayuso return NULL; 34c8d7b98bec43faaa6583c3135030be5eb4693acbPablo Neira Ayuso 35c8d7b98bec43faaa6583c3135030be5eb4693acbPablo Neira Ayuso /* Check checksum */ 36c8d7b98bec43faaa6583c3135030be5eb4693acbPablo Neira Ayuso if (nf_ip_checksum(oldskb, hook, ip_hdrlen(oldskb), IPPROTO_TCP)) 37052b9498eea532deb5de75277a53f6e0623215dcPablo Neira Ayuso return NULL; 38c8d7b98bec43faaa6583c3135030be5eb4693acbPablo Neira Ayuso 39052b9498eea532deb5de75277a53f6e0623215dcPablo Neira Ayuso return oth; 40052b9498eea532deb5de75277a53f6e0623215dcPablo Neira Ayuso} 41052b9498eea532deb5de75277a53f6e0623215dcPablo Neira AyusoEXPORT_SYMBOL_GPL(nf_reject_ip_tcphdr_get); 42c8d7b98bec43faaa6583c3135030be5eb4693acbPablo Neira Ayuso 43052b9498eea532deb5de75277a53f6e0623215dcPablo Neira Ayusostruct iphdr *nf_reject_iphdr_put(struct sk_buff *nskb, 44052b9498eea532deb5de75277a53f6e0623215dcPablo Neira Ayuso const struct sk_buff *oldskb, 45052b9498eea532deb5de75277a53f6e0623215dcPablo Neira Ayuso __be16 protocol, int ttl) 46052b9498eea532deb5de75277a53f6e0623215dcPablo Neira Ayuso{ 47052b9498eea532deb5de75277a53f6e0623215dcPablo Neira Ayuso struct iphdr *niph, *oiph = ip_hdr(oldskb); 48c8d7b98bec43faaa6583c3135030be5eb4693acbPablo Neira Ayuso 49c8d7b98bec43faaa6583c3135030be5eb4693acbPablo Neira Ayuso skb_reset_network_header(nskb); 50c8d7b98bec43faaa6583c3135030be5eb4693acbPablo Neira Ayuso niph = (struct iphdr *)skb_put(nskb, sizeof(struct iphdr)); 51c8d7b98bec43faaa6583c3135030be5eb4693acbPablo Neira Ayuso niph->version = 4; 52c8d7b98bec43faaa6583c3135030be5eb4693acbPablo Neira Ayuso niph->ihl = sizeof(struct iphdr) / 4; 53c8d7b98bec43faaa6583c3135030be5eb4693acbPablo Neira Ayuso niph->tos = 0; 54c8d7b98bec43faaa6583c3135030be5eb4693acbPablo Neira Ayuso niph->id = 0; 55c8d7b98bec43faaa6583c3135030be5eb4693acbPablo Neira Ayuso niph->frag_off = htons(IP_DF); 56052b9498eea532deb5de75277a53f6e0623215dcPablo Neira Ayuso niph->protocol = protocol; 57c8d7b98bec43faaa6583c3135030be5eb4693acbPablo Neira Ayuso niph->check = 0; 58c8d7b98bec43faaa6583c3135030be5eb4693acbPablo Neira Ayuso niph->saddr = oiph->daddr; 59c8d7b98bec43faaa6583c3135030be5eb4693acbPablo Neira Ayuso niph->daddr = oiph->saddr; 60052b9498eea532deb5de75277a53f6e0623215dcPablo Neira Ayuso niph->ttl = ttl; 61052b9498eea532deb5de75277a53f6e0623215dcPablo Neira Ayuso 62052b9498eea532deb5de75277a53f6e0623215dcPablo Neira Ayuso nskb->protocol = htons(ETH_P_IP); 63052b9498eea532deb5de75277a53f6e0623215dcPablo Neira Ayuso 64052b9498eea532deb5de75277a53f6e0623215dcPablo Neira Ayuso return niph; 65052b9498eea532deb5de75277a53f6e0623215dcPablo Neira Ayuso} 66052b9498eea532deb5de75277a53f6e0623215dcPablo Neira AyusoEXPORT_SYMBOL_GPL(nf_reject_iphdr_put); 67052b9498eea532deb5de75277a53f6e0623215dcPablo Neira Ayuso 68052b9498eea532deb5de75277a53f6e0623215dcPablo Neira Ayusovoid nf_reject_ip_tcphdr_put(struct sk_buff *nskb, const struct sk_buff *oldskb, 69052b9498eea532deb5de75277a53f6e0623215dcPablo Neira Ayuso const struct tcphdr *oth) 70052b9498eea532deb5de75277a53f6e0623215dcPablo Neira Ayuso{ 71052b9498eea532deb5de75277a53f6e0623215dcPablo Neira Ayuso struct iphdr *niph = ip_hdr(nskb); 72052b9498eea532deb5de75277a53f6e0623215dcPablo Neira Ayuso struct tcphdr *tcph; 73c8d7b98bec43faaa6583c3135030be5eb4693acbPablo Neira Ayuso 74c8d7b98bec43faaa6583c3135030be5eb4693acbPablo Neira Ayuso skb_reset_transport_header(nskb); 75c8d7b98bec43faaa6583c3135030be5eb4693acbPablo Neira Ayuso tcph = (struct tcphdr *)skb_put(nskb, sizeof(struct tcphdr)); 76c8d7b98bec43faaa6583c3135030be5eb4693acbPablo Neira Ayuso memset(tcph, 0, sizeof(*tcph)); 77c8d7b98bec43faaa6583c3135030be5eb4693acbPablo Neira Ayuso tcph->source = oth->dest; 78c8d7b98bec43faaa6583c3135030be5eb4693acbPablo Neira Ayuso tcph->dest = oth->source; 79c8d7b98bec43faaa6583c3135030be5eb4693acbPablo Neira Ayuso tcph->doff = sizeof(struct tcphdr) / 4; 80c8d7b98bec43faaa6583c3135030be5eb4693acbPablo Neira Ayuso 81052b9498eea532deb5de75277a53f6e0623215dcPablo Neira Ayuso if (oth->ack) { 82c8d7b98bec43faaa6583c3135030be5eb4693acbPablo Neira Ayuso tcph->seq = oth->ack_seq; 83052b9498eea532deb5de75277a53f6e0623215dcPablo Neira Ayuso } else { 84c8d7b98bec43faaa6583c3135030be5eb4693acbPablo Neira Ayuso tcph->ack_seq = htonl(ntohl(oth->seq) + oth->syn + oth->fin + 85c8d7b98bec43faaa6583c3135030be5eb4693acbPablo Neira Ayuso oldskb->len - ip_hdrlen(oldskb) - 86c8d7b98bec43faaa6583c3135030be5eb4693acbPablo Neira Ayuso (oth->doff << 2)); 87c8d7b98bec43faaa6583c3135030be5eb4693acbPablo Neira Ayuso tcph->ack = 1; 88c8d7b98bec43faaa6583c3135030be5eb4693acbPablo Neira Ayuso } 89c8d7b98bec43faaa6583c3135030be5eb4693acbPablo Neira Ayuso 90c8d7b98bec43faaa6583c3135030be5eb4693acbPablo Neira Ayuso tcph->rst = 1; 91c8d7b98bec43faaa6583c3135030be5eb4693acbPablo Neira Ayuso tcph->check = ~tcp_v4_check(sizeof(struct tcphdr), niph->saddr, 92c8d7b98bec43faaa6583c3135030be5eb4693acbPablo Neira Ayuso niph->daddr, 0); 93c8d7b98bec43faaa6583c3135030be5eb4693acbPablo Neira Ayuso nskb->ip_summed = CHECKSUM_PARTIAL; 94c8d7b98bec43faaa6583c3135030be5eb4693acbPablo Neira Ayuso nskb->csum_start = (unsigned char *)tcph - nskb->head; 95c8d7b98bec43faaa6583c3135030be5eb4693acbPablo Neira Ayuso nskb->csum_offset = offsetof(struct tcphdr, check); 96052b9498eea532deb5de75277a53f6e0623215dcPablo Neira Ayuso} 97052b9498eea532deb5de75277a53f6e0623215dcPablo Neira AyusoEXPORT_SYMBOL_GPL(nf_reject_ip_tcphdr_put); 98052b9498eea532deb5de75277a53f6e0623215dcPablo Neira Ayuso 99052b9498eea532deb5de75277a53f6e0623215dcPablo Neira Ayuso/* Send RST reply */ 100052b9498eea532deb5de75277a53f6e0623215dcPablo Neira Ayusovoid nf_send_reset(struct sk_buff *oldskb, int hook) 101052b9498eea532deb5de75277a53f6e0623215dcPablo Neira Ayuso{ 102052b9498eea532deb5de75277a53f6e0623215dcPablo Neira Ayuso struct sk_buff *nskb; 103052b9498eea532deb5de75277a53f6e0623215dcPablo Neira Ayuso const struct iphdr *oiph; 104052b9498eea532deb5de75277a53f6e0623215dcPablo Neira Ayuso struct iphdr *niph; 105052b9498eea532deb5de75277a53f6e0623215dcPablo Neira Ayuso const struct tcphdr *oth; 106052b9498eea532deb5de75277a53f6e0623215dcPablo Neira Ayuso struct tcphdr _oth; 107052b9498eea532deb5de75277a53f6e0623215dcPablo Neira Ayuso 108052b9498eea532deb5de75277a53f6e0623215dcPablo Neira Ayuso oth = nf_reject_ip_tcphdr_get(oldskb, &_oth, hook); 109052b9498eea532deb5de75277a53f6e0623215dcPablo Neira Ayuso if (!oth) 110052b9498eea532deb5de75277a53f6e0623215dcPablo Neira Ayuso return; 111052b9498eea532deb5de75277a53f6e0623215dcPablo Neira Ayuso 112052b9498eea532deb5de75277a53f6e0623215dcPablo Neira Ayuso if (skb_rtable(oldskb)->rt_flags & (RTCF_BROADCAST | RTCF_MULTICAST)) 113052b9498eea532deb5de75277a53f6e0623215dcPablo Neira Ayuso return; 114052b9498eea532deb5de75277a53f6e0623215dcPablo Neira Ayuso 115052b9498eea532deb5de75277a53f6e0623215dcPablo Neira Ayuso oiph = ip_hdr(oldskb); 116052b9498eea532deb5de75277a53f6e0623215dcPablo Neira Ayuso 117052b9498eea532deb5de75277a53f6e0623215dcPablo Neira Ayuso nskb = alloc_skb(sizeof(struct iphdr) + sizeof(struct tcphdr) + 118052b9498eea532deb5de75277a53f6e0623215dcPablo Neira Ayuso LL_MAX_HEADER, GFP_ATOMIC); 119052b9498eea532deb5de75277a53f6e0623215dcPablo Neira Ayuso if (!nskb) 120052b9498eea532deb5de75277a53f6e0623215dcPablo Neira Ayuso return; 121c8d7b98bec43faaa6583c3135030be5eb4693acbPablo Neira Ayuso 122c8d7b98bec43faaa6583c3135030be5eb4693acbPablo Neira Ayuso /* ip_route_me_harder expects skb->dst to be set */ 123c8d7b98bec43faaa6583c3135030be5eb4693acbPablo Neira Ayuso skb_dst_set_noref(nskb, skb_dst(oldskb)); 124c8d7b98bec43faaa6583c3135030be5eb4693acbPablo Neira Ayuso 125052b9498eea532deb5de75277a53f6e0623215dcPablo Neira Ayuso skb_reserve(nskb, LL_MAX_HEADER); 126052b9498eea532deb5de75277a53f6e0623215dcPablo Neira Ayuso niph = nf_reject_iphdr_put(nskb, oldskb, IPPROTO_TCP, 127052b9498eea532deb5de75277a53f6e0623215dcPablo Neira Ayuso ip4_dst_hoplimit(skb_dst(nskb))); 128052b9498eea532deb5de75277a53f6e0623215dcPablo Neira Ayuso nf_reject_ip_tcphdr_put(nskb, oldskb, oth); 129052b9498eea532deb5de75277a53f6e0623215dcPablo Neira Ayuso 130c8d7b98bec43faaa6583c3135030be5eb4693acbPablo Neira Ayuso if (ip_route_me_harder(nskb, RTN_UNSPEC)) 131c8d7b98bec43faaa6583c3135030be5eb4693acbPablo Neira Ayuso goto free_nskb; 132c8d7b98bec43faaa6583c3135030be5eb4693acbPablo Neira Ayuso 133c8d7b98bec43faaa6583c3135030be5eb4693acbPablo Neira Ayuso /* "Never happens" */ 134c8d7b98bec43faaa6583c3135030be5eb4693acbPablo Neira Ayuso if (nskb->len > dst_mtu(skb_dst(nskb))) 135c8d7b98bec43faaa6583c3135030be5eb4693acbPablo Neira Ayuso goto free_nskb; 136c8d7b98bec43faaa6583c3135030be5eb4693acbPablo Neira Ayuso 137c8d7b98bec43faaa6583c3135030be5eb4693acbPablo Neira Ayuso nf_ct_attach(nskb, oldskb); 138c8d7b98bec43faaa6583c3135030be5eb4693acbPablo Neira Ayuso 139c8d7b98bec43faaa6583c3135030be5eb4693acbPablo Neira Ayuso#if IS_ENABLED(CONFIG_BRIDGE_NETFILTER) 140c8d7b98bec43faaa6583c3135030be5eb4693acbPablo Neira Ayuso /* If we use ip_local_out for bridged traffic, the MAC source on 141c8d7b98bec43faaa6583c3135030be5eb4693acbPablo Neira Ayuso * the RST will be ours, instead of the destination's. This confuses 142c8d7b98bec43faaa6583c3135030be5eb4693acbPablo Neira Ayuso * some routers/firewalls, and they drop the packet. So we need to 143c8d7b98bec43faaa6583c3135030be5eb4693acbPablo Neira Ayuso * build the eth header using the original destination's MAC as the 144c8d7b98bec43faaa6583c3135030be5eb4693acbPablo Neira Ayuso * source, and send the RST packet directly. 145c8d7b98bec43faaa6583c3135030be5eb4693acbPablo Neira Ayuso */ 146c8d7b98bec43faaa6583c3135030be5eb4693acbPablo Neira Ayuso if (oldskb->nf_bridge) { 147c8d7b98bec43faaa6583c3135030be5eb4693acbPablo Neira Ayuso struct ethhdr *oeth = eth_hdr(oldskb); 148c8d7b98bec43faaa6583c3135030be5eb4693acbPablo Neira Ayuso nskb->dev = oldskb->nf_bridge->physindev; 149c8d7b98bec43faaa6583c3135030be5eb4693acbPablo Neira Ayuso niph->tot_len = htons(nskb->len); 150c8d7b98bec43faaa6583c3135030be5eb4693acbPablo Neira Ayuso ip_send_check(niph); 151c8d7b98bec43faaa6583c3135030be5eb4693acbPablo Neira Ayuso if (dev_hard_header(nskb, nskb->dev, ntohs(nskb->protocol), 152c8d7b98bec43faaa6583c3135030be5eb4693acbPablo Neira Ayuso oeth->h_source, oeth->h_dest, nskb->len) < 0) 153c8d7b98bec43faaa6583c3135030be5eb4693acbPablo Neira Ayuso goto free_nskb; 154c8d7b98bec43faaa6583c3135030be5eb4693acbPablo Neira Ayuso dev_queue_xmit(nskb); 155c8d7b98bec43faaa6583c3135030be5eb4693acbPablo Neira Ayuso } else 156c8d7b98bec43faaa6583c3135030be5eb4693acbPablo Neira Ayuso#endif 157c8d7b98bec43faaa6583c3135030be5eb4693acbPablo Neira Ayuso ip_local_out(nskb); 158c8d7b98bec43faaa6583c3135030be5eb4693acbPablo Neira Ayuso 159c8d7b98bec43faaa6583c3135030be5eb4693acbPablo Neira Ayuso return; 160c8d7b98bec43faaa6583c3135030be5eb4693acbPablo Neira Ayuso 161c8d7b98bec43faaa6583c3135030be5eb4693acbPablo Neira Ayuso free_nskb: 162c8d7b98bec43faaa6583c3135030be5eb4693acbPablo Neira Ayuso kfree_skb(nskb); 163c8d7b98bec43faaa6583c3135030be5eb4693acbPablo Neira Ayuso} 164c8d7b98bec43faaa6583c3135030be5eb4693acbPablo Neira AyusoEXPORT_SYMBOL_GPL(nf_send_reset); 165ab2d7251d666995740da17b2a51ca545ac5dd037Pablo Neira Ayuso 166ab2d7251d666995740da17b2a51ca545ac5dd037Pablo Neira AyusoMODULE_LICENSE("GPL"); 167