Skip to content

Commit

Permalink
[NETFILTER] ip6tables: remove duplicate code
Browse files Browse the repository at this point in the history
Some IPv6 matches have very similar loops to find IPv6 extension header
and we can unify them. This patch introduces ipv6_find_hdr() to do it.
I just checked that it can find the target headers in the packet which has
dst,hbh,rt,frag,ah,esp headers.

Signed-off-by: Yasuyuki Kozakai <yasuyuki.kozakai@toshiba.co.jp>
Signed-off-by: Harald Welte <laforge@netfilter.org>
Signed-off-by: David S. Miller <davem@davemloft.net>
  • Loading branch information
Yasuyuki Kozakai authored and David S. Miller committed Sep 19, 2005
1 parent 926b50f commit e674d0f
Show file tree
Hide file tree
Showing 8 changed files with 94 additions and 464 deletions.
3 changes: 3 additions & 0 deletions include/linux/netfilter_ipv6/ip6_tables.h
Original file line number Diff line number Diff line change
Expand Up @@ -455,6 +455,9 @@ extern unsigned int ip6t_do_table(struct sk_buff **pskb,

/* Check for an extension */
extern int ip6t_ext_hdr(u8 nexthdr);
/* find specified header and get offset to it */
extern int ipv6_find_hdr(const struct sk_buff *skb, unsigned int *offset,
u8 target);

#define IP6T_ALIGN(s) (((s) + (__alignof__(struct ip6t_entry)-1)) & ~(__alignof__(struct ip6t_entry)-1))

Expand Down
52 changes: 52 additions & 0 deletions net/ipv6/netfilter/ip6_tables.c
Original file line number Diff line number Diff line change
Expand Up @@ -1955,6 +1955,57 @@ static void __exit fini(void)
#endif
}

/*
* find specified header up to transport protocol header.
* If found target header, the offset to the header is set to *offset
* and return 0. otherwise, return -1.
*
* Notes: - non-1st Fragment Header isn't skipped.
* - ESP header isn't skipped.
* - The target header may be trancated.
*/
int ipv6_find_hdr(const struct sk_buff *skb, unsigned int *offset, u8 target)
{
unsigned int start = (u8*)(skb->nh.ipv6h + 1) - skb->data;
u8 nexthdr = skb->nh.ipv6h->nexthdr;
unsigned int len = skb->len - start;

while (nexthdr != target) {
struct ipv6_opt_hdr _hdr, *hp;
unsigned int hdrlen;

if ((!ipv6_ext_hdr(nexthdr)) || nexthdr == NEXTHDR_NONE)
return -1;
hp = skb_header_pointer(skb, start, sizeof(_hdr), &_hdr);
if (hp == NULL)
return -1;
if (nexthdr == NEXTHDR_FRAGMENT) {
unsigned short _frag_off, *fp;
fp = skb_header_pointer(skb,
start+offsetof(struct frag_hdr,
frag_off),
sizeof(_frag_off),
&_frag_off);
if (fp == NULL)
return -1;

if (ntohs(*fp) & ~0x7)
return -1;
hdrlen = 8;
} else if (nexthdr == NEXTHDR_AUTH)
hdrlen = (hp->hdrlen + 2) << 2;
else
hdrlen = ipv6_optlen(hp);

nexthdr = hp->nexthdr;
len -= hdrlen;
start += hdrlen;
}

*offset = start;
return 0;
}

EXPORT_SYMBOL(ip6t_register_table);
EXPORT_SYMBOL(ip6t_unregister_table);
EXPORT_SYMBOL(ip6t_do_table);
Expand All @@ -1963,6 +2014,7 @@ EXPORT_SYMBOL(ip6t_unregister_match);
EXPORT_SYMBOL(ip6t_register_target);
EXPORT_SYMBOL(ip6t_unregister_target);
EXPORT_SYMBOL(ip6t_ext_hdr);
EXPORT_SYMBOL(ipv6_find_hdr);

module_init(init);
module_exit(fini);
81 changes: 5 additions & 76 deletions net/ipv6/netfilter/ip6t_ah.c
Original file line number Diff line number Diff line change
Expand Up @@ -48,92 +48,21 @@ match(const struct sk_buff *skb,
unsigned int protoff,
int *hotdrop)
{
struct ip_auth_hdr *ah = NULL, _ah;
struct ip_auth_hdr *ah, _ah;
const struct ip6t_ah *ahinfo = matchinfo;
unsigned int temp;
int len;
u8 nexthdr;
unsigned int ptr;
unsigned int hdrlen = 0;

/*DEBUGP("IPv6 AH entered\n");*/
/* if (opt->auth == 0) return 0;
* It does not filled on output */

/* type of the 1st exthdr */
nexthdr = skb->nh.ipv6h->nexthdr;
/* pointer to the 1st exthdr */
ptr = sizeof(struct ipv6hdr);
/* available length */
len = skb->len - ptr;
temp = 0;

while (ip6t_ext_hdr(nexthdr)) {
struct ipv6_opt_hdr _hdr, *hp;

DEBUGP("ipv6_ah header iteration \n");

/* Is there enough space for the next ext header? */
if (len < sizeof(struct ipv6_opt_hdr))
return 0;
/* No more exthdr -> evaluate */
if (nexthdr == NEXTHDR_NONE)
break;
/* ESP -> evaluate */
if (nexthdr == NEXTHDR_ESP)
break;

hp = skb_header_pointer(skb, ptr, sizeof(_hdr), &_hdr);
BUG_ON(hp == NULL);

/* Calculate the header length */
if (nexthdr == NEXTHDR_FRAGMENT)
hdrlen = 8;
else if (nexthdr == NEXTHDR_AUTH)
hdrlen = (hp->hdrlen+2)<<2;
else
hdrlen = ipv6_optlen(hp);

/* AH -> evaluate */
if (nexthdr == NEXTHDR_AUTH) {
temp |= MASK_AH;
break;
}


/* set the flag */
switch (nexthdr) {
case NEXTHDR_HOP:
case NEXTHDR_ROUTING:
case NEXTHDR_FRAGMENT:
case NEXTHDR_AUTH:
case NEXTHDR_DEST:
break;
default:
DEBUGP("ipv6_ah match: unknown nextheader %u\n",nexthdr);
return 0;
}

nexthdr = hp->nexthdr;
len -= hdrlen;
ptr += hdrlen;
if (ptr > skb->len) {
DEBUGP("ipv6_ah: new pointer too large! \n");
break;
}
}

/* AH header not found */
if (temp != MASK_AH)
if (ipv6_find_hdr(skb, &ptr, NEXTHDR_AUTH) < 0)
return 0;

if (len < sizeof(struct ip_auth_hdr)){
ah = skb_header_pointer(skb, ptr, sizeof(_ah), &_ah);
if (ah == NULL) {
*hotdrop = 1;
return 0;
}

ah = skb_header_pointer(skb, ptr, sizeof(_ah), &_ah);
BUG_ON(ah == NULL);
hdrlen = (ah->hdrlen + 2) << 2;

DEBUGP("IPv6 AH LEN %u %u ", hdrlen, ah->hdrlen);
DEBUGP("RES %04X ", ah->reserved);
Expand Down
88 changes: 7 additions & 81 deletions net/ipv6/netfilter/ip6t_dst.c
Original file line number Diff line number Diff line change
Expand Up @@ -63,106 +63,32 @@ match(const struct sk_buff *skb,
struct ipv6_opt_hdr _optsh, *oh;
const struct ip6t_opts *optinfo = matchinfo;
unsigned int temp;
unsigned int len;
u8 nexthdr;
unsigned int ptr;
unsigned int hdrlen = 0;
unsigned int ret = 0;
u8 _opttype, *tp = NULL;
u8 _optlen, *lp = NULL;
unsigned int optlen;

/* type of the 1st exthdr */
nexthdr = skb->nh.ipv6h->nexthdr;
/* pointer to the 1st exthdr */
ptr = sizeof(struct ipv6hdr);
/* available length */
len = skb->len - ptr;
temp = 0;

while (ip6t_ext_hdr(nexthdr)) {
struct ipv6_opt_hdr _hdr, *hp;

DEBUGP("ipv6_opts header iteration \n");

/* Is there enough space for the next ext header? */
if (len < (int)sizeof(struct ipv6_opt_hdr))
return 0;
/* No more exthdr -> evaluate */
if (nexthdr == NEXTHDR_NONE) {
break;
}
/* ESP -> evaluate */
if (nexthdr == NEXTHDR_ESP) {
break;
}

hp = skb_header_pointer(skb, ptr, sizeof(_hdr), &_hdr);
BUG_ON(hp == NULL);

/* Calculate the header length */
if (nexthdr == NEXTHDR_FRAGMENT) {
hdrlen = 8;
} else if (nexthdr == NEXTHDR_AUTH)
hdrlen = (hp->hdrlen+2)<<2;
else
hdrlen = ipv6_optlen(hp);

/* OPTS -> evaluate */
#if HOPBYHOP
if (nexthdr == NEXTHDR_HOP) {
temp |= MASK_HOPOPTS;
if (ipv6_find_hdr(skb, &ptr, NEXTHDR_HOP) < 0)
#else
if (nexthdr == NEXTHDR_DEST) {
temp |= MASK_DSTOPTS;
if (ipv6_find_hdr(skb, &ptr, NEXTHDR_DEST) < 0)
#endif
break;
}

return 0;

/* set the flag */
switch (nexthdr){
case NEXTHDR_HOP:
case NEXTHDR_ROUTING:
case NEXTHDR_FRAGMENT:
case NEXTHDR_AUTH:
case NEXTHDR_DEST:
break;
default:
DEBUGP("ipv6_opts match: unknown nextheader %u\n",nexthdr);
return 0;
break;
}

nexthdr = hp->nexthdr;
len -= hdrlen;
ptr += hdrlen;
if ( ptr > skb->len ) {
DEBUGP("ipv6_opts: new pointer is too large! \n");
break;
}
}

/* OPTIONS header not found */
#if HOPBYHOP
if ( temp != MASK_HOPOPTS ) return 0;
#else
if ( temp != MASK_DSTOPTS ) return 0;
#endif

if (len < (int)sizeof(struct ipv6_opt_hdr)){
oh = skb_header_pointer(skb, ptr, sizeof(_optsh), &_optsh);
if (oh == NULL){
*hotdrop = 1;
return 0;
}

if (len < hdrlen){
hdrlen = ipv6_optlen(oh);
if (skb->len - ptr < hdrlen){
/* Packet smaller than it's length field */
return 0;
}

oh = skb_header_pointer(skb, ptr, sizeof(_optsh), &_optsh);
BUG_ON(oh == NULL);

DEBUGP("IPv6 OPTS LEN %u %u ", hdrlen, oh->hdrlen);

DEBUGP("len %02X %04X %02X ",
Expand Down
73 changes: 4 additions & 69 deletions net/ipv6/netfilter/ip6t_esp.c
Original file line number Diff line number Diff line change
Expand Up @@ -48,87 +48,22 @@ match(const struct sk_buff *skb,
unsigned int protoff,
int *hotdrop)
{
struct ip_esp_hdr _esp, *eh = NULL;
struct ip_esp_hdr _esp, *eh;
const struct ip6t_esp *espinfo = matchinfo;
unsigned int temp;
int len;
u8 nexthdr;
unsigned int ptr;

/* Make sure this isn't an evil packet */
/*DEBUGP("ipv6_esp entered \n");*/

/* type of the 1st exthdr */
nexthdr = skb->nh.ipv6h->nexthdr;
/* pointer to the 1st exthdr */
ptr = sizeof(struct ipv6hdr);
/* available length */
len = skb->len - ptr;
temp = 0;

while (ip6t_ext_hdr(nexthdr)) {
struct ipv6_opt_hdr _hdr, *hp;
int hdrlen;

DEBUGP("ipv6_esp header iteration \n");

/* Is there enough space for the next ext header? */
if (len < sizeof(struct ipv6_opt_hdr))
return 0;
/* No more exthdr -> evaluate */
if (nexthdr == NEXTHDR_NONE)
break;
/* ESP -> evaluate */
if (nexthdr == NEXTHDR_ESP) {
temp |= MASK_ESP;
break;
}

hp = skb_header_pointer(skb, ptr, sizeof(_hdr), &_hdr);
BUG_ON(hp == NULL);

/* Calculate the header length */
if (nexthdr == NEXTHDR_FRAGMENT)
hdrlen = 8;
else if (nexthdr == NEXTHDR_AUTH)
hdrlen = (hp->hdrlen+2)<<2;
else
hdrlen = ipv6_optlen(hp);

/* set the flag */
switch (nexthdr) {
case NEXTHDR_HOP:
case NEXTHDR_ROUTING:
case NEXTHDR_FRAGMENT:
case NEXTHDR_AUTH:
case NEXTHDR_DEST:
break;
default:
DEBUGP("ipv6_esp match: unknown nextheader %u\n",nexthdr);
return 0;
}

nexthdr = hp->nexthdr;
len -= hdrlen;
ptr += hdrlen;
if (ptr > skb->len) {
DEBUGP("ipv6_esp: new pointer too large! \n");
break;
}
}

/* ESP header not found */
if (temp != MASK_ESP)
if (ipv6_find_hdr(skb, &ptr, NEXTHDR_ESP) < 0)
return 0;

if (len < sizeof(struct ip_esp_hdr)) {
eh = skb_header_pointer(skb, ptr, sizeof(_esp), &_esp);
if (eh == NULL) {
*hotdrop = 1;
return 0;
}

eh = skb_header_pointer(skb, ptr, sizeof(_esp), &_esp);
BUG_ON(eh == NULL);

DEBUGP("IPv6 ESP SPI %u %08X\n", ntohl(eh->spi), ntohl(eh->spi));

return (eh != NULL)
Expand Down
Loading

0 comments on commit e674d0f

Please sign in to comment.