netfilter: ip6_tables: add flags parameter to ipv6_find_hdr()

This patch adds the flags parameter to ipv6_find_hdr. This flags
allows us to:

* know if this is a fragment.
* stop at the AH header, so the information contained in that header
  can be used for some specific packet handling.

This patch also adds the offset parameter for inspection of one
inner IPv6 header that is contained in error messages.

Signed-off-by: Hans Schillstrom <hans.schillstrom@ericsson.com>
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
This commit is contained in:
Hans Schillstrom 2012-04-23 03:35:26 +00:00 committed by Pablo Neira Ayuso
parent 9bb862beb6
commit 84018f55ab
8 changed files with 49 additions and 18 deletions

View File

@ -298,9 +298,14 @@ ip6t_ext_hdr(u8 nexthdr)
(nexthdr == IPPROTO_DSTOPTS); (nexthdr == IPPROTO_DSTOPTS);
} }
enum {
IP6T_FH_F_FRAG = (1 << 0),
IP6T_FH_F_AUTH = (1 << 1),
};
/* find specified header and get offset to it */ /* find specified header and get offset to it */
extern int ipv6_find_hdr(const struct sk_buff *skb, unsigned int *offset, extern int ipv6_find_hdr(const struct sk_buff *skb, unsigned int *offset,
int target, unsigned short *fragoff); int target, unsigned short *fragoff, int *fragflg);
#ifdef CONFIG_COMPAT #ifdef CONFIG_COMPAT
#include <net/compat.h> #include <net/compat.h>

View File

@ -133,7 +133,7 @@ ip6_packet_match(const struct sk_buff *skb,
int protohdr; int protohdr;
unsigned short _frag_off; unsigned short _frag_off;
protohdr = ipv6_find_hdr(skb, protoff, -1, &_frag_off); protohdr = ipv6_find_hdr(skb, protoff, -1, &_frag_off, NULL);
if (protohdr < 0) { if (protohdr < 0) {
if (_frag_off == 0) if (_frag_off == 0)
*hotdrop = true; *hotdrop = true;
@ -362,6 +362,7 @@ ip6t_do_table(struct sk_buff *skb,
const struct xt_entry_match *ematch; const struct xt_entry_match *ematch;
IP_NF_ASSERT(e); IP_NF_ASSERT(e);
acpar.thoff = 0;
if (!ip6_packet_match(skb, indev, outdev, &e->ipv6, if (!ip6_packet_match(skb, indev, outdev, &e->ipv6,
&acpar.thoff, &acpar.fragoff, &acpar.hotdrop)) { &acpar.thoff, &acpar.fragoff, &acpar.hotdrop)) {
no_match: no_match:
@ -2278,6 +2279,10 @@ static void __exit ip6_tables_fini(void)
* if target < 0. "last header" is transport protocol header, ESP, or * if target < 0. "last header" is transport protocol header, ESP, or
* "No next header". * "No next header".
* *
* Note that *offset is used as input/output parameter. an if it is not zero,
* then it must be a valid offset to an inner IPv6 header. This can be used
* to explore inner IPv6 header, eg. ICMPv6 error messages.
*
* If target header is found, its offset is set in *offset and return protocol * If target header is found, its offset is set in *offset and return protocol
* number. Otherwise, return -1. * number. Otherwise, return -1.
* *
@ -2289,17 +2294,33 @@ static void __exit ip6_tables_fini(void)
* *offset is meaningless and fragment offset is stored in *fragoff if fragoff * *offset is meaningless and fragment offset is stored in *fragoff if fragoff
* isn't NULL. * isn't NULL.
* *
* if flags is not NULL and it's a fragment, then the frag flag IP6T_FH_F_FRAG
* will be set. If it's an AH header, the IP6T_FH_F_AUTH flag is set and
* target < 0, then this function will stop at the AH header.
*/ */
int ipv6_find_hdr(const struct sk_buff *skb, unsigned int *offset, int ipv6_find_hdr(const struct sk_buff *skb, unsigned int *offset,
int target, unsigned short *fragoff) int target, unsigned short *fragoff, int *flags)
{ {
unsigned int start = skb_network_offset(skb) + sizeof(struct ipv6hdr); unsigned int start = skb_network_offset(skb) + sizeof(struct ipv6hdr);
u8 nexthdr = ipv6_hdr(skb)->nexthdr; u8 nexthdr = ipv6_hdr(skb)->nexthdr;
unsigned int len = skb->len - start; unsigned int len;
if (fragoff) if (fragoff)
*fragoff = 0; *fragoff = 0;
if (*offset) {
struct ipv6hdr _ip6, *ip6;
ip6 = skb_header_pointer(skb, *offset, sizeof(_ip6), &_ip6);
if (!ip6 || (ip6->version != 6)) {
printk(KERN_ERR "IPv6 header not found\n");
return -EBADMSG;
}
start = *offset + sizeof(struct ipv6hdr);
nexthdr = ip6->nexthdr;
}
len = skb->len - start;
while (nexthdr != target) { while (nexthdr != target) {
struct ipv6_opt_hdr _hdr, *hp; struct ipv6_opt_hdr _hdr, *hp;
unsigned int hdrlen; unsigned int hdrlen;
@ -2316,6 +2337,9 @@ int ipv6_find_hdr(const struct sk_buff *skb, unsigned int *offset,
if (nexthdr == NEXTHDR_FRAGMENT) { if (nexthdr == NEXTHDR_FRAGMENT) {
unsigned short _frag_off; unsigned short _frag_off;
__be16 *fp; __be16 *fp;
if (flags) /* Indicate that this is a fragment */
*flags |= IP6T_FH_F_FRAG;
fp = skb_header_pointer(skb, fp = skb_header_pointer(skb,
start+offsetof(struct frag_hdr, start+offsetof(struct frag_hdr,
frag_off), frag_off),
@ -2336,9 +2360,11 @@ int ipv6_find_hdr(const struct sk_buff *skb, unsigned int *offset,
return -ENOENT; return -ENOENT;
} }
hdrlen = 8; hdrlen = 8;
} else if (nexthdr == NEXTHDR_AUTH) } else if (nexthdr == NEXTHDR_AUTH) {
if (flags && (*flags & IP6T_FH_F_AUTH) && (target < 0))
break;
hdrlen = (hp->hdrlen + 2) << 2; hdrlen = (hp->hdrlen + 2) << 2;
else } else
hdrlen = ipv6_optlen(hp); hdrlen = ipv6_optlen(hp);
nexthdr = hp->nexthdr; nexthdr = hp->nexthdr;

View File

@ -41,11 +41,11 @@ static bool ah_mt6(const struct sk_buff *skb, struct xt_action_param *par)
struct ip_auth_hdr _ah; struct ip_auth_hdr _ah;
const struct ip_auth_hdr *ah; const struct ip_auth_hdr *ah;
const struct ip6t_ah *ahinfo = par->matchinfo; const struct ip6t_ah *ahinfo = par->matchinfo;
unsigned int ptr; unsigned int ptr = 0;
unsigned int hdrlen = 0; unsigned int hdrlen = 0;
int err; int err;
err = ipv6_find_hdr(skb, &ptr, NEXTHDR_AUTH, NULL); err = ipv6_find_hdr(skb, &ptr, NEXTHDR_AUTH, NULL, NULL);
if (err < 0) { if (err < 0) {
if (err != -ENOENT) if (err != -ENOENT)
par->hotdrop = true; par->hotdrop = true;

View File

@ -40,10 +40,10 @@ frag_mt6(const struct sk_buff *skb, struct xt_action_param *par)
struct frag_hdr _frag; struct frag_hdr _frag;
const struct frag_hdr *fh; const struct frag_hdr *fh;
const struct ip6t_frag *fraginfo = par->matchinfo; const struct ip6t_frag *fraginfo = par->matchinfo;
unsigned int ptr; unsigned int ptr = 0;
int err; int err;
err = ipv6_find_hdr(skb, &ptr, NEXTHDR_FRAGMENT, NULL); err = ipv6_find_hdr(skb, &ptr, NEXTHDR_FRAGMENT, NULL, NULL);
if (err < 0) { if (err < 0) {
if (err != -ENOENT) if (err != -ENOENT)
par->hotdrop = true; par->hotdrop = true;

View File

@ -50,7 +50,7 @@ hbh_mt6(const struct sk_buff *skb, struct xt_action_param *par)
const struct ipv6_opt_hdr *oh; const struct ipv6_opt_hdr *oh;
const struct ip6t_opts *optinfo = par->matchinfo; const struct ip6t_opts *optinfo = par->matchinfo;
unsigned int temp; unsigned int temp;
unsigned int ptr; unsigned int ptr = 0;
unsigned int hdrlen = 0; unsigned int hdrlen = 0;
bool ret = false; bool ret = false;
u8 _opttype; u8 _opttype;
@ -62,7 +62,7 @@ hbh_mt6(const struct sk_buff *skb, struct xt_action_param *par)
err = ipv6_find_hdr(skb, &ptr, err = ipv6_find_hdr(skb, &ptr,
(par->match == &hbh_mt6_reg[0]) ? (par->match == &hbh_mt6_reg[0]) ?
NEXTHDR_HOP : NEXTHDR_DEST, NULL); NEXTHDR_HOP : NEXTHDR_DEST, NULL, NULL);
if (err < 0) { if (err < 0) {
if (err != -ENOENT) if (err != -ENOENT)
par->hotdrop = true; par->hotdrop = true;

View File

@ -42,14 +42,14 @@ static bool rt_mt6(const struct sk_buff *skb, struct xt_action_param *par)
const struct ipv6_rt_hdr *rh; const struct ipv6_rt_hdr *rh;
const struct ip6t_rt *rtinfo = par->matchinfo; const struct ip6t_rt *rtinfo = par->matchinfo;
unsigned int temp; unsigned int temp;
unsigned int ptr; unsigned int ptr = 0;
unsigned int hdrlen = 0; unsigned int hdrlen = 0;
bool ret = false; bool ret = false;
struct in6_addr _addr; struct in6_addr _addr;
const struct in6_addr *ap; const struct in6_addr *ap;
int err; int err;
err = ipv6_find_hdr(skb, &ptr, NEXTHDR_ROUTING, NULL); err = ipv6_find_hdr(skb, &ptr, NEXTHDR_ROUTING, NULL, NULL);
if (err < 0) { if (err < 0) {
if (err != -ENOENT) if (err != -ENOENT)
par->hotdrop = true; par->hotdrop = true;

View File

@ -282,10 +282,10 @@ tproxy_tg6_v1(struct sk_buff *skb, const struct xt_action_param *par)
struct sock *sk; struct sock *sk;
const struct in6_addr *laddr; const struct in6_addr *laddr;
__be16 lport; __be16 lport;
int thoff; int thoff = 0;
int tproto; int tproto;
tproto = ipv6_find_hdr(skb, &thoff, -1, NULL); tproto = ipv6_find_hdr(skb, &thoff, -1, NULL, NULL);
if (tproto < 0) { if (tproto < 0) {
pr_debug("unable to find transport header in IPv6 packet, dropping\n"); pr_debug("unable to find transport header in IPv6 packet, dropping\n");
return NF_DROP; return NF_DROP;

View File

@ -263,10 +263,10 @@ socket_mt6_v1(const struct sk_buff *skb, struct xt_action_param *par)
struct sock *sk; struct sock *sk;
struct in6_addr *daddr, *saddr; struct in6_addr *daddr, *saddr;
__be16 dport, sport; __be16 dport, sport;
int thoff, tproto; int thoff = 0, tproto;
const struct xt_socket_mtinfo1 *info = (struct xt_socket_mtinfo1 *) par->matchinfo; const struct xt_socket_mtinfo1 *info = (struct xt_socket_mtinfo1 *) par->matchinfo;
tproto = ipv6_find_hdr(skb, &thoff, -1, NULL); tproto = ipv6_find_hdr(skb, &thoff, -1, NULL, NULL);
if (tproto < 0) { if (tproto < 0) {
pr_debug("unable to find transport header in IPv6 packet, dropping\n"); pr_debug("unable to find transport header in IPv6 packet, dropping\n");
return NF_DROP; return NF_DROP;