import of upstream 2.4.34.4 from kernel.org
[linux-2.4.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 #include <linux/module.h>
8 #include <linux/skbuff.h>
9 #include <linux/ipv6.h>
10 #include <linux/types.h>
11 #include <net/checksum.h>
12 #include <net/ipv6.h>
13
14 #include <linux/netfilter_ipv6/ip6_tables.h>
15 #include <linux/netfilter_ipv6/ip6t_ipv6header.h>
16
17 EXPORT_NO_SYMBOLS;
18 MODULE_LICENSE("GPL");
19 MODULE_DESCRIPTION("IPv6 headers match");
20 MODULE_AUTHOR("Andras Kis-Szabo <kisza@sch.bme.hu>");
21
22 static int
23 ipv6header_match(const struct sk_buff *skb,
24                  const struct net_device *in,
25                  const struct net_device *out,
26                  const void *matchinfo,
27                  int offset,
28                  const void *protohdr,
29                  u_int16_t datalen,
30                  int *hotdrop)
31 {
32         const struct ip6t_ipv6header_info *info = matchinfo;
33         unsigned int temp;
34         int len;
35         u8 nexthdr;
36         unsigned int ptr;
37
38         /* Make sure this isn't an evil packet */
39
40         /* type of the 1st exthdr */
41         nexthdr = skb->nh.ipv6h->nexthdr;
42         /* pointer to the 1st exthdr */
43         ptr = sizeof(struct ipv6hdr);
44         /* available length */
45         len = skb->len - ptr;
46         temp = 0;
47
48         while (ip6t_ext_hdr(nexthdr)) {
49                 struct ipv6_opt_hdr *hdr;
50                 int hdrlen;
51
52                 /* Is there enough space for the next ext header? */
53                 if (len < (int)sizeof(struct ipv6_opt_hdr))
54                         return 0;
55                 /* No more exthdr -> evaluate */
56                 if (nexthdr == NEXTHDR_NONE) {
57                         temp |= MASK_NONE;
58                         break;
59                 }
60                 /* ESP -> evaluate */
61                 if (nexthdr == NEXTHDR_ESP) {
62                         temp |= MASK_ESP;
63                         break;
64                 }
65
66                 hdr=(struct ipv6_opt_hdr *)(skb->data+ptr);
67
68                 /* Calculate the header length */
69                 if (nexthdr == NEXTHDR_FRAGMENT) {
70                         hdrlen = 8;
71                 } else if (nexthdr == NEXTHDR_AUTH)
72                         hdrlen = (hdr->hdrlen+2)<<2;
73                 else
74                         hdrlen = ipv6_optlen(hdr);
75
76                 /* set the flag */
77                 switch (nexthdr){
78                         case NEXTHDR_HOP:
79                                 temp |= MASK_HOPOPTS;
80                                 break;
81                         case NEXTHDR_ROUTING:
82                                 temp |= MASK_ROUTING;
83                                 break;
84                         case NEXTHDR_FRAGMENT:
85                                 temp |= MASK_FRAGMENT;
86                                 break;
87                         case NEXTHDR_AUTH:
88                                 temp |= MASK_AH;
89                                 break;
90                         case NEXTHDR_DEST:
91                                 temp |= MASK_DSTOPTS;
92                                 break;
93                         default:
94                                 return 0;
95                                 break;
96                 }
97
98                 nexthdr = hdr->nexthdr;
99                 len -= hdrlen;
100                 ptr += hdrlen;
101                 if ( ptr > skb->len ) {
102                         break;
103                 }
104         }
105
106         if ( (nexthdr != NEXTHDR_NONE ) && (nexthdr != NEXTHDR_ESP) )
107                 temp |= MASK_PROTO;
108
109         if (info->modeflag)
110                 return !((temp ^ info->matchflags ^ info->invflags)
111                          & info->matchflags);
112         else {
113                 if (info->invflags)
114                         return temp != info->matchflags;
115                 else
116                         return temp == info->matchflags;
117         }
118 }
119
120 static int
121 ipv6header_checkentry(const char *tablename,
122                       const struct ip6t_ip6 *ip,
123                       void *matchinfo,
124                       unsigned int matchsize,
125                       unsigned int hook_mask)
126 {
127         const struct ip6t_ipv6header_info *info = matchinfo;
128
129         /* Check for obvious errors */
130         /* This match is valid in all hooks! */
131         if (matchsize != IP6T_ALIGN(sizeof(struct ip6t_ipv6header_info)))
132                 return 0;
133
134         /* invflags is 0 or 0xff in hard mode */
135         if ((!info->modeflag) && info->invflags != 0x00
136                               && info->invflags != 0xFF)
137                 return 0;
138
139         return 1;
140 }
141
142 static struct ip6t_match
143 ip6t_ipv6header_match = {
144         { NULL, NULL },
145         "ipv6header",
146         &ipv6header_match,
147         &ipv6header_checkentry,
148         NULL,
149         THIS_MODULE
150 };
151
152 static int  __init ipv6header_init(void)
153 {
154         return ip6t_register_match(&ip6t_ipv6header_match);
155 }
156
157 static void __exit ipv6header_exit(void)
158 {
159         ip6t_unregister_match(&ip6t_ipv6header_match);
160 }
161
162 module_init(ipv6header_init);
163 module_exit(ipv6header_exit);
164