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