1d2912cb1SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
2c8d7b98bSPablo Neira Ayuso /* (C) 1999-2001 Paul `Rusty' Russell
3c8d7b98bSPablo Neira Ayuso  * (C) 2002-2004 Netfilter Core Team <coreteam@netfilter.org>
4c8d7b98bSPablo Neira Ayuso  */
5c8d7b98bSPablo Neira Ayuso 
6ab2d7251SPablo Neira Ayuso #include <linux/module.h>
7c8d7b98bSPablo Neira Ayuso #include <net/ip.h>
8c8d7b98bSPablo Neira Ayuso #include <net/tcp.h>
9c8d7b98bSPablo Neira Ayuso #include <net/route.h>
10c8d7b98bSPablo Neira Ayuso #include <net/dst.h>
1156768644SFlorian Westphal #include <net/netfilter/ipv4/nf_reject.h>
12c8d7b98bSPablo Neira Ayuso #include <linux/netfilter_ipv4.h>
13c737b7c4SFlorian Westphal #include <linux/netfilter_bridge.h>
14c8d7b98bSPablo Neira Ayuso 
nf_reject_iphdr_validate(struct sk_buff * skb)15fa538f7cSJose M. Guisado Gomez static int nf_reject_iphdr_validate(struct sk_buff *skb)
16fa538f7cSJose M. Guisado Gomez {
17fa538f7cSJose M. Guisado Gomez 	struct iphdr *iph;
18fa538f7cSJose M. Guisado Gomez 	u32 len;
19fa538f7cSJose M. Guisado Gomez 
20fa538f7cSJose M. Guisado Gomez 	if (!pskb_may_pull(skb, sizeof(struct iphdr)))
21fa538f7cSJose M. Guisado Gomez 		return 0;
22fa538f7cSJose M. Guisado Gomez 
23fa538f7cSJose M. Guisado Gomez 	iph = ip_hdr(skb);
24fa538f7cSJose M. Guisado Gomez 	if (iph->ihl < 5 || iph->version != 4)
25fa538f7cSJose M. Guisado Gomez 		return 0;
26fa538f7cSJose M. Guisado Gomez 
27fa538f7cSJose M. Guisado Gomez 	len = ntohs(iph->tot_len);
28fa538f7cSJose M. Guisado Gomez 	if (skb->len < len)
29fa538f7cSJose M. Guisado Gomez 		return 0;
30fa538f7cSJose M. Guisado Gomez 	else if (len < (iph->ihl*4))
31fa538f7cSJose M. Guisado Gomez 		return 0;
32fa538f7cSJose M. Guisado Gomez 
33fa538f7cSJose M. Guisado Gomez 	if (!pskb_may_pull(skb, iph->ihl*4))
34fa538f7cSJose M. Guisado Gomez 		return 0;
35fa538f7cSJose M. Guisado Gomez 
36fa538f7cSJose M. Guisado Gomez 	return 1;
37fa538f7cSJose M. Guisado Gomez }
38fa538f7cSJose M. Guisado Gomez 
nf_reject_skb_v4_tcp_reset(struct net * net,struct sk_buff * oldskb,const struct net_device * dev,int hook)39fa538f7cSJose M. Guisado Gomez struct sk_buff *nf_reject_skb_v4_tcp_reset(struct net *net,
40fa538f7cSJose M. Guisado Gomez 					   struct sk_buff *oldskb,
41fa538f7cSJose M. Guisado Gomez 					   const struct net_device *dev,
42fa538f7cSJose M. Guisado Gomez 					   int hook)
43fa538f7cSJose M. Guisado Gomez {
44fa538f7cSJose M. Guisado Gomez 	const struct tcphdr *oth;
45fa538f7cSJose M. Guisado Gomez 	struct sk_buff *nskb;
46fa538f7cSJose M. Guisado Gomez 	struct iphdr *niph;
47fa538f7cSJose M. Guisado Gomez 	struct tcphdr _oth;
48fa538f7cSJose M. Guisado Gomez 
49fa538f7cSJose M. Guisado Gomez 	if (!nf_reject_iphdr_validate(oldskb))
50fa538f7cSJose M. Guisado Gomez 		return NULL;
51fa538f7cSJose M. Guisado Gomez 
52fa538f7cSJose M. Guisado Gomez 	oth = nf_reject_ip_tcphdr_get(oldskb, &_oth, hook);
53fa538f7cSJose M. Guisado Gomez 	if (!oth)
54fa538f7cSJose M. Guisado Gomez 		return NULL;
55fa538f7cSJose M. Guisado Gomez 
56fa538f7cSJose M. Guisado Gomez 	nskb = alloc_skb(sizeof(struct iphdr) + sizeof(struct tcphdr) +
57fa538f7cSJose M. Guisado Gomez 			 LL_MAX_HEADER, GFP_ATOMIC);
58fa538f7cSJose M. Guisado Gomez 	if (!nskb)
59fa538f7cSJose M. Guisado Gomez 		return NULL;
60fa538f7cSJose M. Guisado Gomez 
61fa538f7cSJose M. Guisado Gomez 	nskb->dev = (struct net_device *)dev;
62fa538f7cSJose M. Guisado Gomez 
63fa538f7cSJose M. Guisado Gomez 	skb_reserve(nskb, LL_MAX_HEADER);
64fa538f7cSJose M. Guisado Gomez 	niph = nf_reject_iphdr_put(nskb, oldskb, IPPROTO_TCP,
658281b7ecSKuniyuki Iwashima 				   READ_ONCE(net->ipv4.sysctl_ip_default_ttl));
66fa538f7cSJose M. Guisado Gomez 	nf_reject_ip_tcphdr_put(nskb, oldskb, oth);
67fa538f7cSJose M. Guisado Gomez 	niph->tot_len = htons(nskb->len);
68fa538f7cSJose M. Guisado Gomez 	ip_send_check(niph);
69fa538f7cSJose M. Guisado Gomez 
70fa538f7cSJose M. Guisado Gomez 	return nskb;
71fa538f7cSJose M. Guisado Gomez }
72fa538f7cSJose M. Guisado Gomez EXPORT_SYMBOL_GPL(nf_reject_skb_v4_tcp_reset);
73fa538f7cSJose M. Guisado Gomez 
nf_reject_skb_v4_unreach(struct net * net,struct sk_buff * oldskb,const struct net_device * dev,int hook,u8 code)74fa538f7cSJose M. Guisado Gomez struct sk_buff *nf_reject_skb_v4_unreach(struct net *net,
75fa538f7cSJose M. Guisado Gomez 					 struct sk_buff *oldskb,
76fa538f7cSJose M. Guisado Gomez 					 const struct net_device *dev,
77fa538f7cSJose M. Guisado Gomez 					 int hook, u8 code)
78fa538f7cSJose M. Guisado Gomez {
79fa538f7cSJose M. Guisado Gomez 	struct sk_buff *nskb;
80fa538f7cSJose M. Guisado Gomez 	struct iphdr *niph;
81fa538f7cSJose M. Guisado Gomez 	struct icmphdr *icmph;
82fa538f7cSJose M. Guisado Gomez 	unsigned int len;
834f9bd530SKevin Mitchell 	int dataoff;
84fa538f7cSJose M. Guisado Gomez 	__wsum csum;
85fa538f7cSJose M. Guisado Gomez 	u8 proto;
86fa538f7cSJose M. Guisado Gomez 
87fa538f7cSJose M. Guisado Gomez 	if (!nf_reject_iphdr_validate(oldskb))
88fa538f7cSJose M. Guisado Gomez 		return NULL;
89fa538f7cSJose M. Guisado Gomez 
90fa538f7cSJose M. Guisado Gomez 	/* IP header checks: fragment. */
91fa538f7cSJose M. Guisado Gomez 	if (ip_hdr(oldskb)->frag_off & htons(IP_OFFSET))
92fa538f7cSJose M. Guisado Gomez 		return NULL;
93fa538f7cSJose M. Guisado Gomez 
94fa538f7cSJose M. Guisado Gomez 	/* RFC says return as much as we can without exceeding 576 bytes. */
95fa538f7cSJose M. Guisado Gomez 	len = min_t(unsigned int, 536, oldskb->len);
96fa538f7cSJose M. Guisado Gomez 
97fa538f7cSJose M. Guisado Gomez 	if (!pskb_may_pull(oldskb, len))
98fa538f7cSJose M. Guisado Gomez 		return NULL;
99fa538f7cSJose M. Guisado Gomez 
100fa538f7cSJose M. Guisado Gomez 	if (pskb_trim_rcsum(oldskb, ntohs(ip_hdr(oldskb)->tot_len)))
101fa538f7cSJose M. Guisado Gomez 		return NULL;
102fa538f7cSJose M. Guisado Gomez 
1034f9bd530SKevin Mitchell 	dataoff = ip_hdrlen(oldskb);
104fa538f7cSJose M. Guisado Gomez 	proto = ip_hdr(oldskb)->protocol;
105fa538f7cSJose M. Guisado Gomez 
106fa538f7cSJose M. Guisado Gomez 	if (!skb_csum_unnecessary(oldskb) &&
1074f9bd530SKevin Mitchell 	    nf_reject_verify_csum(oldskb, dataoff, proto) &&
108fa538f7cSJose M. Guisado Gomez 	    nf_ip_checksum(oldskb, hook, ip_hdrlen(oldskb), proto))
109fa538f7cSJose M. Guisado Gomez 		return NULL;
110fa538f7cSJose M. Guisado Gomez 
111fa538f7cSJose M. Guisado Gomez 	nskb = alloc_skb(sizeof(struct iphdr) + sizeof(struct icmphdr) +
112fa538f7cSJose M. Guisado Gomez 			 LL_MAX_HEADER + len, GFP_ATOMIC);
113fa538f7cSJose M. Guisado Gomez 	if (!nskb)
114fa538f7cSJose M. Guisado Gomez 		return NULL;
115fa538f7cSJose M. Guisado Gomez 
116fa538f7cSJose M. Guisado Gomez 	nskb->dev = (struct net_device *)dev;
117fa538f7cSJose M. Guisado Gomez 
118fa538f7cSJose M. Guisado Gomez 	skb_reserve(nskb, LL_MAX_HEADER);
119fa538f7cSJose M. Guisado Gomez 	niph = nf_reject_iphdr_put(nskb, oldskb, IPPROTO_ICMP,
1208281b7ecSKuniyuki Iwashima 				   READ_ONCE(net->ipv4.sysctl_ip_default_ttl));
121fa538f7cSJose M. Guisado Gomez 
122fa538f7cSJose M. Guisado Gomez 	skb_reset_transport_header(nskb);
123fa538f7cSJose M. Guisado Gomez 	icmph = skb_put_zero(nskb, sizeof(struct icmphdr));
124fa538f7cSJose M. Guisado Gomez 	icmph->type     = ICMP_DEST_UNREACH;
125fa538f7cSJose M. Guisado Gomez 	icmph->code	= code;
126fa538f7cSJose M. Guisado Gomez 
127fa538f7cSJose M. Guisado Gomez 	skb_put_data(nskb, skb_network_header(oldskb), len);
128fa538f7cSJose M. Guisado Gomez 
129fa538f7cSJose M. Guisado Gomez 	csum = csum_partial((void *)icmph, len + sizeof(struct icmphdr), 0);
130fa538f7cSJose M. Guisado Gomez 	icmph->checksum = csum_fold(csum);
131fa538f7cSJose M. Guisado Gomez 
132fa538f7cSJose M. Guisado Gomez 	niph->tot_len	= htons(nskb->len);
133fa538f7cSJose M. Guisado Gomez 	ip_send_check(niph);
134fa538f7cSJose M. Guisado Gomez 
135fa538f7cSJose M. Guisado Gomez 	return nskb;
136fa538f7cSJose M. Guisado Gomez }
137fa538f7cSJose M. Guisado Gomez EXPORT_SYMBOL_GPL(nf_reject_skb_v4_unreach);
138fa538f7cSJose M. Guisado Gomez 
nf_reject_ip_tcphdr_get(struct sk_buff * oldskb,struct tcphdr * _oth,int hook)139052b9498SPablo Neira Ayuso const struct tcphdr *nf_reject_ip_tcphdr_get(struct sk_buff *oldskb,
140052b9498SPablo Neira Ayuso 					     struct tcphdr *_oth, int hook)
141c8d7b98bSPablo Neira Ayuso {
142c8d7b98bSPablo Neira Ayuso 	const struct tcphdr *oth;
143c8d7b98bSPablo Neira Ayuso 
144c8d7b98bSPablo Neira Ayuso 	/* IP header checks: fragment. */
145c8d7b98bSPablo Neira Ayuso 	if (ip_hdr(oldskb)->frag_off & htons(IP_OFFSET))
146052b9498SPablo Neira Ayuso 		return NULL;
147c8d7b98bSPablo Neira Ayuso 
148e1dbbc59SLiping Zhang 	if (ip_hdr(oldskb)->protocol != IPPROTO_TCP)
149e1dbbc59SLiping Zhang 		return NULL;
150e1dbbc59SLiping Zhang 
151c8d7b98bSPablo Neira Ayuso 	oth = skb_header_pointer(oldskb, ip_hdrlen(oldskb),
152052b9498SPablo Neira Ayuso 				 sizeof(struct tcphdr), _oth);
153c8d7b98bSPablo Neira Ayuso 	if (oth == NULL)
154052b9498SPablo Neira Ayuso 		return NULL;
155c8d7b98bSPablo Neira Ayuso 
156c8d7b98bSPablo Neira Ayuso 	/* No RST for RST. */
157c8d7b98bSPablo Neira Ayuso 	if (oth->rst)
158052b9498SPablo Neira Ayuso 		return NULL;
159c8d7b98bSPablo Neira Ayuso 
160c8d7b98bSPablo Neira Ayuso 	/* Check checksum */
161c8d7b98bSPablo Neira Ayuso 	if (nf_ip_checksum(oldskb, hook, ip_hdrlen(oldskb), IPPROTO_TCP))
162052b9498SPablo Neira Ayuso 		return NULL;
163c8d7b98bSPablo Neira Ayuso 
164052b9498SPablo Neira Ayuso 	return oth;
165052b9498SPablo Neira Ayuso }
166052b9498SPablo Neira Ayuso EXPORT_SYMBOL_GPL(nf_reject_ip_tcphdr_get);
167c8d7b98bSPablo Neira Ayuso 
nf_reject_iphdr_put(struct sk_buff * nskb,const struct sk_buff * oldskb,__u8 protocol,int ttl)168052b9498SPablo Neira Ayuso struct iphdr *nf_reject_iphdr_put(struct sk_buff *nskb,
169052b9498SPablo Neira Ayuso 				  const struct sk_buff *oldskb,
170a03a8dbeSFlorian Westphal 				  __u8 protocol, int ttl)
171052b9498SPablo Neira Ayuso {
172052b9498SPablo Neira Ayuso 	struct iphdr *niph, *oiph = ip_hdr(oldskb);
173c8d7b98bSPablo Neira Ayuso 
174c8d7b98bSPablo Neira Ayuso 	skb_reset_network_header(nskb);
1754df864c1SJohannes Berg 	niph = skb_put(nskb, sizeof(struct iphdr));
176c8d7b98bSPablo Neira Ayuso 	niph->version	= 4;
177c8d7b98bSPablo Neira Ayuso 	niph->ihl	= sizeof(struct iphdr) / 4;
178c8d7b98bSPablo Neira Ayuso 	niph->tos	= 0;
179c8d7b98bSPablo Neira Ayuso 	niph->id	= 0;
180c8d7b98bSPablo Neira Ayuso 	niph->frag_off	= htons(IP_DF);
181052b9498SPablo Neira Ayuso 	niph->protocol	= protocol;
182c8d7b98bSPablo Neira Ayuso 	niph->check	= 0;
183c8d7b98bSPablo Neira Ayuso 	niph->saddr	= oiph->daddr;
184c8d7b98bSPablo Neira Ayuso 	niph->daddr	= oiph->saddr;
185052b9498SPablo Neira Ayuso 	niph->ttl	= ttl;
186052b9498SPablo Neira Ayuso 
187052b9498SPablo Neira Ayuso 	nskb->protocol = htons(ETH_P_IP);
188052b9498SPablo Neira Ayuso 
189052b9498SPablo Neira Ayuso 	return niph;
190052b9498SPablo Neira Ayuso }
191052b9498SPablo Neira Ayuso EXPORT_SYMBOL_GPL(nf_reject_iphdr_put);
192052b9498SPablo Neira Ayuso 
nf_reject_ip_tcphdr_put(struct sk_buff * nskb,const struct sk_buff * oldskb,const struct tcphdr * oth)193052b9498SPablo Neira Ayuso void nf_reject_ip_tcphdr_put(struct sk_buff *nskb, const struct sk_buff *oldskb,
194052b9498SPablo Neira Ayuso 			  const struct tcphdr *oth)
195052b9498SPablo Neira Ayuso {
196052b9498SPablo Neira Ayuso 	struct iphdr *niph = ip_hdr(nskb);
197052b9498SPablo Neira Ayuso 	struct tcphdr *tcph;
198c8d7b98bSPablo Neira Ayuso 
199c8d7b98bSPablo Neira Ayuso 	skb_reset_transport_header(nskb);
200b080db58SJohannes Berg 	tcph = skb_put_zero(nskb, sizeof(struct tcphdr));
201c8d7b98bSPablo Neira Ayuso 	tcph->source	= oth->dest;
202c8d7b98bSPablo Neira Ayuso 	tcph->dest	= oth->source;
203c8d7b98bSPablo Neira Ayuso 	tcph->doff	= sizeof(struct tcphdr) / 4;
204c8d7b98bSPablo Neira Ayuso 
205052b9498SPablo Neira Ayuso 	if (oth->ack) {
206c8d7b98bSPablo Neira Ayuso 		tcph->seq = oth->ack_seq;
207052b9498SPablo Neira Ayuso 	} else {
208c8d7b98bSPablo Neira Ayuso 		tcph->ack_seq = htonl(ntohl(oth->seq) + oth->syn + oth->fin +
209c8d7b98bSPablo Neira Ayuso 				      oldskb->len - ip_hdrlen(oldskb) -
210c8d7b98bSPablo Neira Ayuso 				      (oth->doff << 2));
211c8d7b98bSPablo Neira Ayuso 		tcph->ack = 1;
212c8d7b98bSPablo Neira Ayuso 	}
213c8d7b98bSPablo Neira Ayuso 
214c8d7b98bSPablo Neira Ayuso 	tcph->rst	= 1;
215c8d7b98bSPablo Neira Ayuso 	tcph->check = ~tcp_v4_check(sizeof(struct tcphdr), niph->saddr,
216c8d7b98bSPablo Neira Ayuso 				    niph->daddr, 0);
217c8d7b98bSPablo Neira Ayuso 	nskb->ip_summed = CHECKSUM_PARTIAL;
218c8d7b98bSPablo Neira Ayuso 	nskb->csum_start = (unsigned char *)tcph - nskb->head;
219c8d7b98bSPablo Neira Ayuso 	nskb->csum_offset = offsetof(struct tcphdr, check);
220052b9498SPablo Neira Ayuso }
221052b9498SPablo Neira Ayuso EXPORT_SYMBOL_GPL(nf_reject_ip_tcphdr_put);
222052b9498SPablo Neira Ayuso 
nf_reject_fill_skb_dst(struct sk_buff * skb_in)223f53b9b0bSLaura Garcia Liebana static int nf_reject_fill_skb_dst(struct sk_buff *skb_in)
224f53b9b0bSLaura Garcia Liebana {
225f53b9b0bSLaura Garcia Liebana 	struct dst_entry *dst = NULL;
226f53b9b0bSLaura Garcia Liebana 	struct flowi fl;
227f53b9b0bSLaura Garcia Liebana 
228f53b9b0bSLaura Garcia Liebana 	memset(&fl, 0, sizeof(struct flowi));
229f53b9b0bSLaura Garcia Liebana 	fl.u.ip4.daddr = ip_hdr(skb_in)->saddr;
230f53b9b0bSLaura Garcia Liebana 	nf_ip_route(dev_net(skb_in->dev), &dst, &fl, false);
231f53b9b0bSLaura Garcia Liebana 	if (!dst)
232f53b9b0bSLaura Garcia Liebana 		return -1;
233f53b9b0bSLaura Garcia Liebana 
234f53b9b0bSLaura Garcia Liebana 	skb_dst_set(skb_in, dst);
235f53b9b0bSLaura Garcia Liebana 	return 0;
236f53b9b0bSLaura Garcia Liebana }
237f53b9b0bSLaura Garcia Liebana 
238052b9498SPablo Neira Ayuso /* Send RST reply */
nf_send_reset(struct net * net,struct sock * sk,struct sk_buff * oldskb,int hook)23904295878SJan Engelhardt void nf_send_reset(struct net *net, struct sock *sk, struct sk_buff *oldskb,
24004295878SJan Engelhardt 		   int hook)
241052b9498SPablo Neira Ayuso {
242052b9498SPablo Neira Ayuso 	struct sk_buff *nskb;
243052b9498SPablo Neira Ayuso 	struct iphdr *niph;
244052b9498SPablo Neira Ayuso 	const struct tcphdr *oth;
245052b9498SPablo Neira Ayuso 	struct tcphdr _oth;
246052b9498SPablo Neira Ayuso 
247052b9498SPablo Neira Ayuso 	oth = nf_reject_ip_tcphdr_get(oldskb, &_oth, hook);
248052b9498SPablo Neira Ayuso 	if (!oth)
249052b9498SPablo Neira Ayuso 		return;
250052b9498SPablo Neira Ayuso 
251117ca1f8SPablo Neira Ayuso 	if ((hook == NF_INET_PRE_ROUTING || hook == NF_INET_INGRESS) &&
252117ca1f8SPablo Neira Ayuso 	    nf_reject_fill_skb_dst(oldskb) < 0)
253f53b9b0bSLaura Garcia Liebana 		return;
254f53b9b0bSLaura Garcia Liebana 
255052b9498SPablo Neira Ayuso 	if (skb_rtable(oldskb)->rt_flags & (RTCF_BROADCAST | RTCF_MULTICAST))
256052b9498SPablo Neira Ayuso 		return;
257052b9498SPablo Neira Ayuso 
258052b9498SPablo Neira Ayuso 	nskb = alloc_skb(sizeof(struct iphdr) + sizeof(struct tcphdr) +
259052b9498SPablo Neira Ayuso 			 LL_MAX_HEADER, GFP_ATOMIC);
260052b9498SPablo Neira Ayuso 	if (!nskb)
261052b9498SPablo Neira Ayuso 		return;
262c8d7b98bSPablo Neira Ayuso 
263c8d7b98bSPablo Neira Ayuso 	/* ip_route_me_harder expects skb->dst to be set */
264c8d7b98bSPablo Neira Ayuso 	skb_dst_set_noref(nskb, skb_dst(oldskb));
265c8d7b98bSPablo Neira Ayuso 
266cc31d43bSPau Espin Pedrol 	nskb->mark = IP4_REPLY_MARK(net, oldskb->mark);
267cc31d43bSPau Espin Pedrol 
268052b9498SPablo Neira Ayuso 	skb_reserve(nskb, LL_MAX_HEADER);
269052b9498SPablo Neira Ayuso 	niph = nf_reject_iphdr_put(nskb, oldskb, IPPROTO_TCP,
270052b9498SPablo Neira Ayuso 				   ip4_dst_hoplimit(skb_dst(nskb)));
271052b9498SPablo Neira Ayuso 	nf_reject_ip_tcphdr_put(nskb, oldskb, oth);
27204295878SJan Engelhardt 	if (ip_route_me_harder(net, sk, nskb, RTN_UNSPEC))
273c8d7b98bSPablo Neira Ayuso 		goto free_nskb;
274c8d7b98bSPablo Neira Ayuso 
2757400bb4bSTejaswi Tanikella 	niph = ip_hdr(nskb);
2767400bb4bSTejaswi Tanikella 
277c8d7b98bSPablo Neira Ayuso 	/* "Never happens" */
278c8d7b98bSPablo Neira Ayuso 	if (nskb->len > dst_mtu(skb_dst(nskb)))
279c8d7b98bSPablo Neira Ayuso 		goto free_nskb;
280c8d7b98bSPablo Neira Ayuso 
281c8d7b98bSPablo Neira Ayuso 	nf_ct_attach(nskb, oldskb);
2822954fe60SFlorian Westphal 	nf_ct_set_closing(skb_nfct(oldskb));
283c8d7b98bSPablo Neira Ayuso 
284c8d7b98bSPablo Neira Ayuso #if IS_ENABLED(CONFIG_BRIDGE_NETFILTER)
285c8d7b98bSPablo Neira Ayuso 	/* If we use ip_local_out for bridged traffic, the MAC source on
286c8d7b98bSPablo Neira Ayuso 	 * the RST will be ours, instead of the destination's.  This confuses
287c8d7b98bSPablo Neira Ayuso 	 * some routers/firewalls, and they drop the packet.  So we need to
288c8d7b98bSPablo Neira Ayuso 	 * build the eth header using the original destination's MAC as the
289c8d7b98bSPablo Neira Ayuso 	 * source, and send the RST packet directly.
290c8d7b98bSPablo Neira Ayuso 	 */
291*9325e318SPavel Tikhomirov 	if (nf_bridge_info_exists(oldskb)) {
292c8d7b98bSPablo Neira Ayuso 		struct ethhdr *oeth = eth_hdr(oldskb);
293*9325e318SPavel Tikhomirov 		struct net_device *br_indev;
294*9325e318SPavel Tikhomirov 
295*9325e318SPavel Tikhomirov 		br_indev = nf_bridge_get_physindev(oldskb, net);
296*9325e318SPavel Tikhomirov 		if (!br_indev)
297*9325e318SPavel Tikhomirov 			goto free_nskb;
298c737b7c4SFlorian Westphal 
299c4b0e771SFlorian Westphal 		nskb->dev = br_indev;
300c8d7b98bSPablo Neira Ayuso 		niph->tot_len = htons(nskb->len);
301c8d7b98bSPablo Neira Ayuso 		ip_send_check(niph);
302c8d7b98bSPablo Neira Ayuso 		if (dev_hard_header(nskb, nskb->dev, ntohs(nskb->protocol),
303c8d7b98bSPablo Neira Ayuso 				    oeth->h_source, oeth->h_dest, nskb->len) < 0)
304c8d7b98bSPablo Neira Ayuso 			goto free_nskb;
305c8d7b98bSPablo Neira Ayuso 		dev_queue_xmit(nskb);
306c8d7b98bSPablo Neira Ayuso 	} else
307c8d7b98bSPablo Neira Ayuso #endif
30833224b16SEric W. Biederman 		ip_local_out(net, nskb->sk, nskb);
309c8d7b98bSPablo Neira Ayuso 
310c8d7b98bSPablo Neira Ayuso 	return;
311c8d7b98bSPablo Neira Ayuso 
312c8d7b98bSPablo Neira Ayuso  free_nskb:
313c8d7b98bSPablo Neira Ayuso 	kfree_skb(nskb);
314c8d7b98bSPablo Neira Ayuso }
315c8d7b98bSPablo Neira Ayuso EXPORT_SYMBOL_GPL(nf_send_reset);
316ab2d7251SPablo Neira Ayuso 
nf_send_unreach(struct sk_buff * skb_in,int code,int hook)317ee586bbcSFlorian Westphal void nf_send_unreach(struct sk_buff *skb_in, int code, int hook)
318ee586bbcSFlorian Westphal {
319ee586bbcSFlorian Westphal 	struct iphdr *iph = ip_hdr(skb_in);
3204f9bd530SKevin Mitchell 	int dataoff = ip_hdrlen(skb_in);
3217fc38225SAlin Nastac 	u8 proto = iph->protocol;
322ee586bbcSFlorian Westphal 
323219f1d79SDavide Caratti 	if (iph->frag_off & htons(IP_OFFSET))
324ee586bbcSFlorian Westphal 		return;
325ee586bbcSFlorian Westphal 
326117ca1f8SPablo Neira Ayuso 	if ((hook == NF_INET_PRE_ROUTING || hook == NF_INET_INGRESS) &&
327117ca1f8SPablo Neira Ayuso 	    nf_reject_fill_skb_dst(skb_in) < 0)
328f53b9b0bSLaura Garcia Liebana 		return;
329f53b9b0bSLaura Garcia Liebana 
3304f9bd530SKevin Mitchell 	if (skb_csum_unnecessary(skb_in) ||
3314f9bd530SKevin Mitchell 	    !nf_reject_verify_csum(skb_in, dataoff, proto)) {
332ee586bbcSFlorian Westphal 		icmp_send(skb_in, ICMP_DEST_UNREACH, code, 0);
333ee586bbcSFlorian Westphal 		return;
334ee586bbcSFlorian Westphal 	}
335ee586bbcSFlorian Westphal 
3364f9bd530SKevin Mitchell 	if (nf_ip_checksum(skb_in, hook, dataoff, proto) == 0)
337ee586bbcSFlorian Westphal 		icmp_send(skb_in, ICMP_DEST_UNREACH, code, 0);
338ee586bbcSFlorian Westphal }
339ee586bbcSFlorian Westphal EXPORT_SYMBOL_GPL(nf_send_unreach);
340ee586bbcSFlorian Westphal 
341ab2d7251SPablo Neira Ayuso MODULE_LICENSE("GPL");
342