Merge branch 'upstream' of git://ftp.linux-mips.org/pub/scm/upstream-linus
[linux-drm-fsl-dcu.git] / net / ipv6 / netfilter / ip6t_ipv6header.c
1 /* ipv6header match - matches IPv6 packets based
2    on whether they contain certain headers */
3
4 /* Original idea: Brad Chapman
5  * Rewritten by: Andras Kis-Szabo <kisza@sch.bme.hu> */
6
7 /* (C) 2001-2002 Andras Kis-Szabo <kisza@sch.bme.hu>
8  *
9  * This program is free software; you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License version 2 as
11  * published by the Free Software Foundation.
12  */
13
14 #include <linux/module.h>
15 #include <linux/skbuff.h>
16 #include <linux/ipv6.h>
17 #include <linux/types.h>
18 #include <net/checksum.h>
19 #include <net/ipv6.h>
20
21 #include <linux/netfilter/x_tables.h>
22 #include <linux/netfilter_ipv6/ip6_tables.h>
23 #include <linux/netfilter_ipv6/ip6t_ipv6header.h>
24
25 MODULE_LICENSE("GPL");
26 MODULE_DESCRIPTION("IPv6 headers match");
27 MODULE_AUTHOR("Andras Kis-Szabo <kisza@sch.bme.hu>");
28
29 static int
30 ipv6header_match(const struct sk_buff *skb,
31                  const struct net_device *in,
32                  const struct net_device *out,
33                  const struct xt_match *match,
34                  const void *matchinfo,
35                  int offset,
36                  unsigned int protoff,
37                  int *hotdrop)
38 {
39         const struct ip6t_ipv6header_info *info = matchinfo;
40         unsigned int temp;
41         int len;
42         u8 nexthdr;
43         unsigned int ptr;
44
45         /* Make sure this isn't an evil packet */
46
47         /* type of the 1st exthdr */
48         nexthdr = skb->nh.ipv6h->nexthdr;
49         /* pointer to the 1st exthdr */
50         ptr = sizeof(struct ipv6hdr);
51         /* available length */
52         len = skb->len - ptr;
53         temp = 0;
54
55         while (ip6t_ext_hdr(nexthdr)) {
56                 struct ipv6_opt_hdr _hdr, *hp;
57                 int hdrlen;
58
59                 /* Is there enough space for the next ext header? */
60                 if (len < (int)sizeof(struct ipv6_opt_hdr))
61                         return 0;
62                 /* No more exthdr -> evaluate */
63                 if (nexthdr == NEXTHDR_NONE) {
64                         temp |= MASK_NONE;
65                         break;
66                 }
67                 /* ESP -> evaluate */
68                 if (nexthdr == NEXTHDR_ESP) {
69                         temp |= MASK_ESP;
70                         break;
71                 }
72
73                 hp = skb_header_pointer(skb, ptr, sizeof(_hdr), &_hdr);
74                 BUG_ON(hp == NULL);
75
76                 /* Calculate the header length */
77                 if (nexthdr == NEXTHDR_FRAGMENT) {
78                         hdrlen = 8;
79                 } else if (nexthdr == NEXTHDR_AUTH)
80                         hdrlen = (hp->hdrlen + 2) << 2;
81                 else
82                         hdrlen = ipv6_optlen(hp);
83
84                 /* set the flag */
85                 switch (nexthdr) {
86                 case NEXTHDR_HOP:
87                         temp |= MASK_HOPOPTS;
88                         break;
89                 case NEXTHDR_ROUTING:
90                         temp |= MASK_ROUTING;
91                         break;
92                 case NEXTHDR_FRAGMENT:
93                         temp |= MASK_FRAGMENT;
94                         break;
95                 case NEXTHDR_AUTH:
96                         temp |= MASK_AH;
97                         break;
98                 case NEXTHDR_DEST:
99                         temp |= MASK_DSTOPTS;
100                         break;
101                 default:
102                         return 0;
103                         break;
104                 }
105
106                 nexthdr = hp->nexthdr;
107                 len -= hdrlen;
108                 ptr += hdrlen;
109                 if (ptr > skb->len)
110                         break;
111         }
112
113         if ((nexthdr != NEXTHDR_NONE) && (nexthdr != NEXTHDR_ESP))
114                 temp |= MASK_PROTO;
115
116         if (info->modeflag)
117                 return !((temp ^ info->matchflags ^ info->invflags)
118                          & info->matchflags);
119         else {
120                 if (info->invflags)
121                         return temp != info->matchflags;
122                 else
123                         return temp == info->matchflags;
124         }
125 }
126
127 static int
128 ipv6header_checkentry(const char *tablename,
129                       const void *ip,
130                       const struct xt_match *match,
131                       void *matchinfo,
132                       unsigned int hook_mask)
133 {
134         const struct ip6t_ipv6header_info *info = matchinfo;
135
136         /* invflags is 0 or 0xff in hard mode */
137         if ((!info->modeflag) && info->invflags != 0x00 &&
138             info->invflags != 0xFF)
139                 return 0;
140
141         return 1;
142 }
143
144 static struct xt_match ip6t_ipv6header_match = {
145         .name           = "ipv6header",
146         .family         = AF_INET6,
147         .match          = &ipv6header_match,
148         .matchsize      = sizeof(struct ip6t_ipv6header_info),
149         .checkentry     = &ipv6header_checkentry,
150         .destroy        = NULL,
151         .me             = THIS_MODULE,
152 };
153
154 static int __init ipv6header_init(void)
155 {
156         return xt_register_match(&ip6t_ipv6header_match);
157 }
158
159 static void __exit ipv6header_exit(void)
160 {
161         xt_unregister_match(&ip6t_ipv6header_match);
162 }
163
164 module_init(ipv6header_init);
165 module_exit(ipv6header_exit);