Commit 8bfcdf66 authored by Pablo Neira Ayuso's avatar Pablo Neira Ayuso

netfilter: nf_reject_ipv6: split nf_send_reset6() in smaller functions

That can be reused by the reject bridge expression to build the reject
packet. The new functions are:

* nf_reject_ip6_tcphdr_get(): to sanitize and to obtain the TCP header.
* nf_reject_ip6hdr_put(): to build the IPv6 header.
* nf_reject_ip6_tcphdr_put(): to build the TCP header.
Signed-off-by: default avatarPablo Neira Ayuso <pablo@netfilter.org>
parent 052b9498
...@@ -15,4 +15,14 @@ nf_send_unreach6(struct net *net, struct sk_buff *skb_in, unsigned char code, ...@@ -15,4 +15,14 @@ nf_send_unreach6(struct net *net, struct sk_buff *skb_in, unsigned char code,
void nf_send_reset6(struct net *net, struct sk_buff *oldskb, int hook); void nf_send_reset6(struct net *net, struct sk_buff *oldskb, int hook);
const struct tcphdr *nf_reject_ip6_tcphdr_get(struct sk_buff *oldskb,
struct tcphdr *otcph,
unsigned int *otcplen, int hook);
struct ipv6hdr *nf_reject_ip6hdr_put(struct sk_buff *nskb,
const struct sk_buff *oldskb,
__be16 protocol, int hoplimit);
void nf_reject_ip6_tcphdr_put(struct sk_buff *nskb,
const struct sk_buff *oldskb,
const struct tcphdr *oth, unsigned int otcplen);
#endif /* _IPV6_NF_REJECT_H */ #endif /* _IPV6_NF_REJECT_H */
...@@ -12,116 +12,102 @@ ...@@ -12,116 +12,102 @@
#include <net/ip6_fib.h> #include <net/ip6_fib.h>
#include <net/ip6_checksum.h> #include <net/ip6_checksum.h>
#include <linux/netfilter_ipv6.h> #include <linux/netfilter_ipv6.h>
#include <net/netfilter/ipv6/nf_reject.h>
void nf_send_reset6(struct net *net, struct sk_buff *oldskb, int hook) const struct tcphdr *nf_reject_ip6_tcphdr_get(struct sk_buff *oldskb,
struct tcphdr *otcph,
unsigned int *otcplen, int hook)
{ {
struct sk_buff *nskb;
struct tcphdr otcph, *tcph;
unsigned int otcplen, hh_len;
int tcphoff, needs_ack;
const struct ipv6hdr *oip6h = ipv6_hdr(oldskb); const struct ipv6hdr *oip6h = ipv6_hdr(oldskb);
struct ipv6hdr *ip6h;
#define DEFAULT_TOS_VALUE 0x0U
const __u8 tclass = DEFAULT_TOS_VALUE;
struct dst_entry *dst = NULL;
u8 proto; u8 proto;
__be16 frag_off; __be16 frag_off;
struct flowi6 fl6; int tcphoff;
if ((!(ipv6_addr_type(&oip6h->saddr) & IPV6_ADDR_UNICAST)) ||
(!(ipv6_addr_type(&oip6h->daddr) & IPV6_ADDR_UNICAST))) {
pr_debug("addr is not unicast.\n");
return;
}
proto = oip6h->nexthdr; proto = oip6h->nexthdr;
tcphoff = ipv6_skip_exthdr(oldskb, ((u8*)(oip6h+1) - oldskb->data), &proto, &frag_off); tcphoff = ipv6_skip_exthdr(oldskb, ((u8*)(oip6h+1) - oldskb->data),
&proto, &frag_off);
if ((tcphoff < 0) || (tcphoff > oldskb->len)) { if ((tcphoff < 0) || (tcphoff > oldskb->len)) {
pr_debug("Cannot get TCP header.\n"); pr_debug("Cannot get TCP header.\n");
return; return NULL;
} }
otcplen = oldskb->len - tcphoff; *otcplen = oldskb->len - tcphoff;
/* IP header checks: fragment, too short. */ /* IP header checks: fragment, too short. */
if (proto != IPPROTO_TCP || otcplen < sizeof(struct tcphdr)) { if (proto != IPPROTO_TCP || *otcplen < sizeof(struct tcphdr)) {
pr_debug("proto(%d) != IPPROTO_TCP, " pr_debug("proto(%d) != IPPROTO_TCP or too short (len = %d)\n",
"or too short. otcplen = %d\n", proto, *otcplen);
proto, otcplen); return NULL;
return;
} }
if (skb_copy_bits(oldskb, tcphoff, &otcph, sizeof(struct tcphdr))) otcph = skb_header_pointer(oldskb, tcphoff, sizeof(struct tcphdr),
BUG(); otcph);
if (otcph == NULL)
return NULL;
/* No RST for RST. */ /* No RST for RST. */
if (otcph.rst) { if (otcph->rst) {
pr_debug("RST is set\n"); pr_debug("RST is set\n");
return; return NULL;
} }
/* Check checksum. */ /* Check checksum. */
if (nf_ip6_checksum(oldskb, hook, tcphoff, IPPROTO_TCP)) { if (nf_ip6_checksum(oldskb, hook, tcphoff, IPPROTO_TCP)) {
pr_debug("TCP checksum is invalid\n"); pr_debug("TCP checksum is invalid\n");
return; return NULL;
}
memset(&fl6, 0, sizeof(fl6));
fl6.flowi6_proto = IPPROTO_TCP;
fl6.saddr = oip6h->daddr;
fl6.daddr = oip6h->saddr;
fl6.fl6_sport = otcph.dest;
fl6.fl6_dport = otcph.source;
security_skb_classify_flow(oldskb, flowi6_to_flowi(&fl6));
dst = ip6_route_output(net, NULL, &fl6);
if (dst == NULL || dst->error) {
dst_release(dst);
return;
}
dst = xfrm_lookup(net, dst, flowi6_to_flowi(&fl6), NULL, 0);
if (IS_ERR(dst))
return;
hh_len = (dst->dev->hard_header_len + 15)&~15;
nskb = alloc_skb(hh_len + 15 + dst->header_len + sizeof(struct ipv6hdr)
+ sizeof(struct tcphdr) + dst->trailer_len,
GFP_ATOMIC);
if (!nskb) {
net_dbg_ratelimited("cannot alloc skb\n");
dst_release(dst);
return;
} }
skb_dst_set(nskb, dst); return otcph;
}
EXPORT_SYMBOL_GPL(nf_reject_ip6_tcphdr_get);
skb_reserve(nskb, hh_len + dst->header_len); struct ipv6hdr *nf_reject_ip6hdr_put(struct sk_buff *nskb,
const struct sk_buff *oldskb,
__be16 protocol, int hoplimit)
{
struct ipv6hdr *ip6h;
const struct ipv6hdr *oip6h = ipv6_hdr(oldskb);
#define DEFAULT_TOS_VALUE 0x0U
const __u8 tclass = DEFAULT_TOS_VALUE;
skb_put(nskb, sizeof(struct ipv6hdr)); skb_put(nskb, sizeof(struct ipv6hdr));
skb_reset_network_header(nskb); skb_reset_network_header(nskb);
ip6h = ipv6_hdr(nskb); ip6h = ipv6_hdr(nskb);
ip6_flow_hdr(ip6h, tclass, 0); ip6_flow_hdr(ip6h, tclass, 0);
ip6h->hop_limit = ip6_dst_hoplimit(dst); ip6h->hop_limit = hoplimit;
ip6h->nexthdr = IPPROTO_TCP; ip6h->nexthdr = protocol;
ip6h->saddr = oip6h->daddr; ip6h->saddr = oip6h->daddr;
ip6h->daddr = oip6h->saddr; ip6h->daddr = oip6h->saddr;
nskb->protocol = htons(ETH_P_IPV6);
return ip6h;
}
EXPORT_SYMBOL_GPL(nf_reject_ip6hdr_put);
void nf_reject_ip6_tcphdr_put(struct sk_buff *nskb,
const struct sk_buff *oldskb,
const struct tcphdr *oth, unsigned int otcplen)
{
struct tcphdr *tcph;
int needs_ack;
skb_reset_transport_header(nskb); skb_reset_transport_header(nskb);
tcph = (struct tcphdr *)skb_put(nskb, sizeof(struct tcphdr)); tcph = (struct tcphdr *)skb_put(nskb, sizeof(struct tcphdr));
/* Truncate to length (no data) */ /* Truncate to length (no data) */
tcph->doff = sizeof(struct tcphdr)/4; tcph->doff = sizeof(struct tcphdr)/4;
tcph->source = otcph.dest; tcph->source = oth->dest;
tcph->dest = otcph.source; tcph->dest = oth->source;
if (otcph.ack) { if (oth->ack) {
needs_ack = 0; needs_ack = 0;
tcph->seq = otcph.ack_seq; tcph->seq = oth->ack_seq;
tcph->ack_seq = 0; tcph->ack_seq = 0;
} else { } else {
needs_ack = 1; needs_ack = 1;
tcph->ack_seq = htonl(ntohl(otcph.seq) + otcph.syn + otcph.fin tcph->ack_seq = htonl(ntohl(oth->seq) + oth->syn + oth->fin +
+ otcplen - (otcph.doff<<2)); otcplen - (oth->doff<<2));
tcph->seq = 0; tcph->seq = 0;
} }
...@@ -139,6 +125,63 @@ void nf_send_reset6(struct net *net, struct sk_buff *oldskb, int hook) ...@@ -139,6 +125,63 @@ void nf_send_reset6(struct net *net, struct sk_buff *oldskb, int hook)
sizeof(struct tcphdr), IPPROTO_TCP, sizeof(struct tcphdr), IPPROTO_TCP,
csum_partial(tcph, csum_partial(tcph,
sizeof(struct tcphdr), 0)); sizeof(struct tcphdr), 0));
}
EXPORT_SYMBOL_GPL(nf_reject_ip6_tcphdr_put);
void nf_send_reset6(struct net *net, struct sk_buff *oldskb, int hook)
{
struct sk_buff *nskb;
struct tcphdr _otcph;
const struct tcphdr *otcph;
unsigned int otcplen, hh_len;
const struct ipv6hdr *oip6h = ipv6_hdr(oldskb);
struct ipv6hdr *ip6h;
struct dst_entry *dst = NULL;
struct flowi6 fl6;
if ((!(ipv6_addr_type(&oip6h->saddr) & IPV6_ADDR_UNICAST)) ||
(!(ipv6_addr_type(&oip6h->daddr) & IPV6_ADDR_UNICAST))) {
pr_debug("addr is not unicast.\n");
return;
}
otcph = nf_reject_ip6_tcphdr_get(oldskb, &_otcph, &otcplen, hook);
if (!otcph)
return;
memset(&fl6, 0, sizeof(fl6));
fl6.flowi6_proto = IPPROTO_TCP;
fl6.saddr = oip6h->daddr;
fl6.daddr = oip6h->saddr;
fl6.fl6_sport = otcph->dest;
fl6.fl6_dport = otcph->source;
security_skb_classify_flow(oldskb, flowi6_to_flowi(&fl6));
dst = ip6_route_output(net, NULL, &fl6);
if (dst == NULL || dst->error) {
dst_release(dst);
return;
}
dst = xfrm_lookup(net, dst, flowi6_to_flowi(&fl6), NULL, 0);
if (IS_ERR(dst))
return;
hh_len = (dst->dev->hard_header_len + 15)&~15;
nskb = alloc_skb(hh_len + 15 + dst->header_len + sizeof(struct ipv6hdr)
+ sizeof(struct tcphdr) + dst->trailer_len,
GFP_ATOMIC);
if (!nskb) {
net_dbg_ratelimited("cannot alloc skb\n");
dst_release(dst);
return;
}
skb_dst_set(nskb, dst);
skb_reserve(nskb, hh_len + dst->header_len);
ip6h = nf_reject_ip6hdr_put(nskb, oldskb, IPPROTO_TCP,
ip6_dst_hoplimit(dst));
nf_reject_ip6_tcphdr_put(nskb, oldskb, otcph, otcplen);
nf_ct_attach(nskb, oldskb); nf_ct_attach(nskb, oldskb);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment