aboutsummaryrefslogtreecommitdiffstats
path: root/include/net
diff options
context:
space:
mode:
authorEric Leblond <eric@regit.org>2013-12-29 06:28:13 -0500
committerPablo Neira Ayuso <pablo@netfilter.org>2013-12-30 09:04:41 -0500
commitcc70d069e2b9cece683206c0f6a1d1484414e577 (patch)
treebe91bf60de5839a92b5f9edf9adb156b91e473a0 /include/net
parent5f291c2869a08a63dbdc9de3e13ac5f2d5ed8d47 (diff)
netfilter: REJECT: separate reusable code
This patch prepares the addition of TCP reset support in the nft_reject module by moving reusable code into a header file. Signed-off-by: Eric Leblond <eric@regit.org> Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
Diffstat (limited to 'include/net')
-rw-r--r--include/net/netfilter/ipv4/nf_reject.h128
-rw-r--r--include/net/netfilter/ipv6/nf_reject.h171
2 files changed, 299 insertions, 0 deletions
diff --git a/include/net/netfilter/ipv4/nf_reject.h b/include/net/netfilter/ipv4/nf_reject.h
new file mode 100644
index 000000000000..931fbf812171
--- /dev/null
+++ b/include/net/netfilter/ipv4/nf_reject.h
@@ -0,0 +1,128 @@
1#ifndef _IPV4_NF_REJECT_H
2#define _IPV4_NF_REJECT_H
3
4#include <net/ip.h>
5#include <net/tcp.h>
6#include <net/route.h>
7#include <net/dst.h>
8
9static inline void nf_send_unreach(struct sk_buff *skb_in, int code)
10{
11 icmp_send(skb_in, ICMP_DEST_UNREACH, code, 0);
12}
13
14/* Send RST reply */
15static void nf_send_reset(struct sk_buff *oldskb, int hook)
16{
17 struct sk_buff *nskb;
18 const struct iphdr *oiph;
19 struct iphdr *niph;
20 const struct tcphdr *oth;
21 struct tcphdr _otcph, *tcph;
22
23 /* IP header checks: fragment. */
24 if (ip_hdr(oldskb)->frag_off & htons(IP_OFFSET))
25 return;
26
27 oth = skb_header_pointer(oldskb, ip_hdrlen(oldskb),
28 sizeof(_otcph), &_otcph);
29 if (oth == NULL)
30 return;
31
32 /* No RST for RST. */
33 if (oth->rst)
34 return;
35
36 if (skb_rtable(oldskb)->rt_flags & (RTCF_BROADCAST | RTCF_MULTICAST))
37 return;
38
39 /* Check checksum */
40 if (nf_ip_checksum(oldskb, hook, ip_hdrlen(oldskb), IPPROTO_TCP))
41 return;
42 oiph = ip_hdr(oldskb);
43
44 nskb = alloc_skb(sizeof(struct iphdr) + sizeof(struct tcphdr) +
45 LL_MAX_HEADER, GFP_ATOMIC);
46 if (!nskb)
47 return;
48
49 skb_reserve(nskb, LL_MAX_HEADER);
50
51 skb_reset_network_header(nskb);
52 niph = (struct iphdr *)skb_put(nskb, sizeof(struct iphdr));
53 niph->version = 4;
54 niph->ihl = sizeof(struct iphdr) / 4;
55 niph->tos = 0;
56 niph->id = 0;
57 niph->frag_off = htons(IP_DF);
58 niph->protocol = IPPROTO_TCP;
59 niph->check = 0;
60 niph->saddr = oiph->daddr;
61 niph->daddr = oiph->saddr;
62
63 skb_reset_transport_header(nskb);
64 tcph = (struct tcphdr *)skb_put(nskb, sizeof(struct tcphdr));
65 memset(tcph, 0, sizeof(*tcph));
66 tcph->source = oth->dest;
67 tcph->dest = oth->source;
68 tcph->doff = sizeof(struct tcphdr) / 4;
69
70 if (oth->ack)
71 tcph->seq = oth->ack_seq;
72 else {
73 tcph->ack_seq = htonl(ntohl(oth->seq) + oth->syn + oth->fin +
74 oldskb->len - ip_hdrlen(oldskb) -
75 (oth->doff << 2));
76 tcph->ack = 1;
77 }
78
79 tcph->rst = 1;
80 tcph->check = ~tcp_v4_check(sizeof(struct tcphdr), niph->saddr,
81 niph->daddr, 0);
82 nskb->ip_summed = CHECKSUM_PARTIAL;
83 nskb->csum_start = (unsigned char *)tcph - nskb->head;
84 nskb->csum_offset = offsetof(struct tcphdr, check);
85
86 /* ip_route_me_harder expects skb->dst to be set */
87 skb_dst_set_noref(nskb, skb_dst(oldskb));
88
89 nskb->protocol = htons(ETH_P_IP);
90 if (ip_route_me_harder(nskb, RTN_UNSPEC))
91 goto free_nskb;
92
93 niph->ttl = ip4_dst_hoplimit(skb_dst(nskb));
94
95 /* "Never happens" */
96 if (nskb->len > dst_mtu(skb_dst(nskb)))
97 goto free_nskb;
98
99 nf_ct_attach(nskb, oldskb);
100
101#ifdef CONFIG_BRIDGE_NETFILTER
102 /* If we use ip_local_out for bridged traffic, the MAC source on
103 * the RST will be ours, instead of the destination's. This confuses
104 * some routers/firewalls, and they drop the packet. So we need to
105 * build the eth header using the original destination's MAC as the
106 * source, and send the RST packet directly.
107 */
108 if (oldskb->nf_bridge) {
109 struct ethhdr *oeth = eth_hdr(oldskb);
110 nskb->dev = oldskb->nf_bridge->physindev;
111 niph->tot_len = htons(nskb->len);
112 ip_send_check(niph);
113 if (dev_hard_header(nskb, nskb->dev, ntohs(nskb->protocol),
114 oeth->h_source, oeth->h_dest, nskb->len) < 0)
115 goto free_nskb;
116 dev_queue_xmit(nskb);
117 } else
118#endif
119 ip_local_out(nskb);
120
121 return;
122
123 free_nskb:
124 kfree_skb(nskb);
125}
126
127
128#endif /* _IPV4_NF_REJECT_H */
diff --git a/include/net/netfilter/ipv6/nf_reject.h b/include/net/netfilter/ipv6/nf_reject.h
new file mode 100644
index 000000000000..710d17ed70b4
--- /dev/null
+++ b/include/net/netfilter/ipv6/nf_reject.h
@@ -0,0 +1,171 @@
1#ifndef _IPV6_NF_REJECT_H
2#define _IPV6_NF_REJECT_H
3
4#include <net/ipv6.h>
5#include <net/ip6_route.h>
6#include <net/ip6_fib.h>
7#include <net/ip6_checksum.h>
8#include <linux/netfilter_ipv6.h>
9
10static inline void
11nf_send_unreach6(struct net *net, struct sk_buff *skb_in, unsigned char code,
12 unsigned int hooknum)
13{
14 if (hooknum == NF_INET_LOCAL_OUT && skb_in->dev == NULL)
15 skb_in->dev = net->loopback_dev;
16
17 icmpv6_send(skb_in, ICMPV6_DEST_UNREACH, code, 0);
18}
19
20/* Send RST reply */
21static void nf_send_reset6(struct net *net, struct sk_buff *oldskb, int hook)
22{
23 struct sk_buff *nskb;
24 struct tcphdr otcph, *tcph;
25 unsigned int otcplen, hh_len;
26 int tcphoff, needs_ack;
27 const struct ipv6hdr *oip6h = ipv6_hdr(oldskb);
28 struct ipv6hdr *ip6h;
29#define DEFAULT_TOS_VALUE 0x0U
30 const __u8 tclass = DEFAULT_TOS_VALUE;
31 struct dst_entry *dst = NULL;
32 u8 proto;
33 __be16 frag_off;
34 struct flowi6 fl6;
35
36 if ((!(ipv6_addr_type(&oip6h->saddr) & IPV6_ADDR_UNICAST)) ||
37 (!(ipv6_addr_type(&oip6h->daddr) & IPV6_ADDR_UNICAST))) {
38 pr_debug("addr is not unicast.\n");
39 return;
40 }
41
42 proto = oip6h->nexthdr;
43 tcphoff = ipv6_skip_exthdr(oldskb, ((u8*)(oip6h+1) - oldskb->data), &proto, &frag_off);
44
45 if ((tcphoff < 0) || (tcphoff > oldskb->len)) {
46 pr_debug("Cannot get TCP header.\n");
47 return;
48 }
49
50 otcplen = oldskb->len - tcphoff;
51
52 /* IP header checks: fragment, too short. */
53 if (proto != IPPROTO_TCP || otcplen < sizeof(struct tcphdr)) {
54 pr_debug("proto(%d) != IPPROTO_TCP, "
55 "or too short. otcplen = %d\n",
56 proto, otcplen);
57 return;
58 }
59
60 if (skb_copy_bits(oldskb, tcphoff, &otcph, sizeof(struct tcphdr)))
61 BUG();
62
63 /* No RST for RST. */
64 if (otcph.rst) {
65 pr_debug("RST is set\n");
66 return;
67 }
68
69 /* Check checksum. */
70 if (nf_ip6_checksum(oldskb, hook, tcphoff, IPPROTO_TCP)) {
71 pr_debug("TCP checksum is invalid\n");
72 return;
73 }
74
75 memset(&fl6, 0, sizeof(fl6));
76 fl6.flowi6_proto = IPPROTO_TCP;
77 fl6.saddr = oip6h->daddr;
78 fl6.daddr = oip6h->saddr;
79 fl6.fl6_sport = otcph.dest;
80 fl6.fl6_dport = otcph.source;
81 security_skb_classify_flow(oldskb, flowi6_to_flowi(&fl6));
82 dst = ip6_route_output(net, NULL, &fl6);
83 if (dst == NULL || dst->error) {
84 dst_release(dst);
85 return;
86 }
87 dst = xfrm_lookup(net, dst, flowi6_to_flowi(&fl6), NULL, 0);
88 if (IS_ERR(dst))
89 return;
90
91 hh_len = (dst->dev->hard_header_len + 15)&~15;
92 nskb = alloc_skb(hh_len + 15 + dst->header_len + sizeof(struct ipv6hdr)
93 + sizeof(struct tcphdr) + dst->trailer_len,
94 GFP_ATOMIC);
95
96 if (!nskb) {
97 net_dbg_ratelimited("cannot alloc skb\n");
98 dst_release(dst);
99 return;
100 }
101
102 skb_dst_set(nskb, dst);
103
104 skb_reserve(nskb, hh_len + dst->header_len);
105
106 skb_put(nskb, sizeof(struct ipv6hdr));
107 skb_reset_network_header(nskb);
108 ip6h = ipv6_hdr(nskb);
109 ip6_flow_hdr(ip6h, tclass, 0);
110 ip6h->hop_limit = ip6_dst_hoplimit(dst);
111 ip6h->nexthdr = IPPROTO_TCP;
112 ip6h->saddr = oip6h->daddr;
113 ip6h->daddr = oip6h->saddr;
114
115 skb_reset_transport_header(nskb);
116 tcph = (struct tcphdr *)skb_put(nskb, sizeof(struct tcphdr));
117 /* Truncate to length (no data) */
118 tcph->doff = sizeof(struct tcphdr)/4;
119 tcph->source = otcph.dest;
120 tcph->dest = otcph.source;
121
122 if (otcph.ack) {
123 needs_ack = 0;
124 tcph->seq = otcph.ack_seq;
125 tcph->ack_seq = 0;
126 } else {
127 needs_ack = 1;
128 tcph->ack_seq = htonl(ntohl(otcph.seq) + otcph.syn + otcph.fin
129 + otcplen - (otcph.doff<<2));
130 tcph->seq = 0;
131 }
132
133 /* Reset flags */
134 ((u_int8_t *)tcph)[13] = 0;
135 tcph->rst = 1;
136 tcph->ack = needs_ack;
137 tcph->window = 0;
138 tcph->urg_ptr = 0;
139 tcph->check = 0;
140
141 /* Adjust TCP checksum */
142 tcph->check = csum_ipv6_magic(&ipv6_hdr(nskb)->saddr,
143 &ipv6_hdr(nskb)->daddr,
144 sizeof(struct tcphdr), IPPROTO_TCP,
145 csum_partial(tcph,
146 sizeof(struct tcphdr), 0));
147
148 nf_ct_attach(nskb, oldskb);
149
150#ifdef CONFIG_BRIDGE_NETFILTER
151 /* If we use ip6_local_out for bridged traffic, the MAC source on
152 * the RST will be ours, instead of the destination's. This confuses
153 * some routers/firewalls, and they drop the packet. So we need to
154 * build the eth header using the original destination's MAC as the
155 * source, and send the RST packet directly.
156 */
157 if (oldskb->nf_bridge) {
158 struct ethhdr *oeth = eth_hdr(oldskb);
159 nskb->dev = oldskb->nf_bridge->physindev;
160 nskb->protocol = htons(ETH_P_IPV6);
161 ip6h->payload_len = htons(sizeof(struct tcphdr));
162 if (dev_hard_header(nskb, nskb->dev, ntohs(nskb->protocol),
163 oeth->h_source, oeth->h_dest, nskb->len) < 0)
164 return;
165 dev_queue_xmit(nskb);
166 } else
167#endif
168 ip6_local_out(nskb);
169}
170
171#endif /* _IPV6_NF_REJECT_H */