1/* (C) 1999-2001 Paul `Rusty' Russell 2 * (C) 2002-2004 Netfilter Core Team <coreteam@netfilter.org> 3 * 4 * This program is free software; you can redistribute it and/or modify 5 * it under the terms of the GNU General Public License version 2 as 6 * published by the Free Software Foundation. 7 */ 8 9#include <linux/module.h> 10#include <net/ip.h> 11#include <net/tcp.h> 12#include <net/route.h> 13#include <net/dst.h> 14#include <linux/netfilter_ipv4.h> 15#include <net/netfilter/ipv4/nf_reject.h> 16 17const struct tcphdr *nf_reject_ip_tcphdr_get(struct sk_buff *oldskb, 18 struct tcphdr *_oth, int hook) 19{ 20 const struct tcphdr *oth; 21 22 /* IP header checks: fragment. */ 23 if (ip_hdr(oldskb)->frag_off & htons(IP_OFFSET)) 24 return NULL; 25 26 oth = skb_header_pointer(oldskb, ip_hdrlen(oldskb), 27 sizeof(struct tcphdr), _oth); 28 if (oth == NULL) 29 return NULL; 30 31 /* No RST for RST. */ 32 if (oth->rst) 33 return NULL; 34 35 /* Check checksum */ 36 if (nf_ip_checksum(oldskb, hook, ip_hdrlen(oldskb), IPPROTO_TCP)) 37 return NULL; 38 39 return oth; 40} 41EXPORT_SYMBOL_GPL(nf_reject_ip_tcphdr_get); 42 43struct iphdr *nf_reject_iphdr_put(struct sk_buff *nskb, 44 const struct sk_buff *oldskb, 45 __be16 protocol, int ttl) 46{ 47 struct iphdr *niph, *oiph = ip_hdr(oldskb); 48 49 skb_reset_network_header(nskb); 50 niph = (struct iphdr *)skb_put(nskb, sizeof(struct iphdr)); 51 niph->version = 4; 52 niph->ihl = sizeof(struct iphdr) / 4; 53 niph->tos = 0; 54 niph->id = 0; 55 niph->frag_off = htons(IP_DF); 56 niph->protocol = protocol; 57 niph->check = 0; 58 niph->saddr = oiph->daddr; 59 niph->daddr = oiph->saddr; 60 niph->ttl = ttl; 61 62 nskb->protocol = htons(ETH_P_IP); 63 64 return niph; 65} 66EXPORT_SYMBOL_GPL(nf_reject_iphdr_put); 67 68void nf_reject_ip_tcphdr_put(struct sk_buff *nskb, const struct sk_buff *oldskb, 69 const struct tcphdr *oth) 70{ 71 struct iphdr *niph = ip_hdr(nskb); 72 struct tcphdr *tcph; 73 74 skb_reset_transport_header(nskb); 75 tcph = (struct tcphdr *)skb_put(nskb, sizeof(struct tcphdr)); 76 memset(tcph, 0, sizeof(*tcph)); 77 tcph->source = oth->dest; 78 tcph->dest = oth->source; 79 tcph->doff = sizeof(struct tcphdr) / 4; 80 81 if (oth->ack) { 82 tcph->seq = oth->ack_seq; 83 } else { 84 tcph->ack_seq = htonl(ntohl(oth->seq) + oth->syn + oth->fin + 85 oldskb->len - ip_hdrlen(oldskb) - 86 (oth->doff << 2)); 87 tcph->ack = 1; 88 } 89 90 tcph->rst = 1; 91 tcph->check = ~tcp_v4_check(sizeof(struct tcphdr), niph->saddr, 92 niph->daddr, 0); 93 nskb->ip_summed = CHECKSUM_PARTIAL; 94 nskb->csum_start = (unsigned char *)tcph - nskb->head; 95 nskb->csum_offset = offsetof(struct tcphdr, check); 96} 97EXPORT_SYMBOL_GPL(nf_reject_ip_tcphdr_put); 98 99/* Send RST reply */ 100void nf_send_reset(struct sk_buff *oldskb, int hook) 101{ 102 struct sk_buff *nskb; 103 const struct iphdr *oiph; 104 struct iphdr *niph; 105 const struct tcphdr *oth; 106 struct tcphdr _oth; 107 108 oth = nf_reject_ip_tcphdr_get(oldskb, &_oth, hook); 109 if (!oth) 110 return; 111 112 if (skb_rtable(oldskb)->rt_flags & (RTCF_BROADCAST | RTCF_MULTICAST)) 113 return; 114 115 oiph = ip_hdr(oldskb); 116 117 nskb = alloc_skb(sizeof(struct iphdr) + sizeof(struct tcphdr) + 118 LL_MAX_HEADER, GFP_ATOMIC); 119 if (!nskb) 120 return; 121 122 /* ip_route_me_harder expects skb->dst to be set */ 123 skb_dst_set_noref(nskb, skb_dst(oldskb)); 124 125 skb_reserve(nskb, LL_MAX_HEADER); 126 niph = nf_reject_iphdr_put(nskb, oldskb, IPPROTO_TCP, 127 ip4_dst_hoplimit(skb_dst(nskb))); 128 nf_reject_ip_tcphdr_put(nskb, oldskb, oth); 129 130 if (ip_route_me_harder(nskb, RTN_UNSPEC)) 131 goto free_nskb; 132 133 /* "Never happens" */ 134 if (nskb->len > dst_mtu(skb_dst(nskb))) 135 goto free_nskb; 136 137 nf_ct_attach(nskb, oldskb); 138 139#if IS_ENABLED(CONFIG_BRIDGE_NETFILTER) 140 /* If we use ip_local_out for bridged traffic, the MAC source on 141 * the RST will be ours, instead of the destination's. This confuses 142 * some routers/firewalls, and they drop the packet. So we need to 143 * build the eth header using the original destination's MAC as the 144 * source, and send the RST packet directly. 145 */ 146 if (oldskb->nf_bridge) { 147 struct ethhdr *oeth = eth_hdr(oldskb); 148 nskb->dev = oldskb->nf_bridge->physindev; 149 niph->tot_len = htons(nskb->len); 150 ip_send_check(niph); 151 if (dev_hard_header(nskb, nskb->dev, ntohs(nskb->protocol), 152 oeth->h_source, oeth->h_dest, nskb->len) < 0) 153 goto free_nskb; 154 dev_queue_xmit(nskb); 155 } else 156#endif 157 ip_local_out(nskb); 158 159 return; 160 161 free_nskb: 162 kfree_skb(nskb); 163} 164EXPORT_SYMBOL_GPL(nf_send_reset); 165 166MODULE_LICENSE("GPL"); 167