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