blob: d5b94f142bbaed3b96d2c939d919ea979e12bc6e [file] [log] [blame]
Linus Torvalds1da177e2005-04-16 15:20:36 -07001/* Kernel module to match AH parameters. */
2
3/* (C) 2001-2002 Andras Kis-Szabo <kisza@sch.bme.hu>
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License version 2 as
7 * published by the Free Software Foundation.
8 */
9
10#include <linux/module.h>
11#include <linux/skbuff.h>
12#include <linux/ipv6.h>
13#include <linux/types.h>
14#include <net/checksum.h>
15#include <net/ipv6.h>
16
17#include <linux/netfilter_ipv6/ip6_tables.h>
18#include <linux/netfilter_ipv6/ip6t_ah.h>
19
20MODULE_LICENSE("GPL");
21MODULE_DESCRIPTION("IPv6 AH match");
22MODULE_AUTHOR("Andras Kis-Szabo <kisza@sch.bme.hu>");
23
24#if 0
25#define DEBUGP printk
26#else
27#define DEBUGP(format, args...)
28#endif
29
30/* Returns 1 if the spi is matched by the range, 0 otherwise */
31static inline int
32spi_match(u_int32_t min, u_int32_t max, u_int32_t spi, int invert)
33{
34 int r=0;
35 DEBUGP("ah spi_match:%c 0x%x <= 0x%x <= 0x%x",invert? '!':' ',
36 min,spi,max);
37 r = (spi >= min && spi <= max) ^ invert;
38 DEBUGP(" result %s\n",r? "PASS\n" : "FAILED\n");
39 return r;
40}
41
42static int
43match(const struct sk_buff *skb,
44 const struct net_device *in,
45 const struct net_device *out,
46 const void *matchinfo,
47 int offset,
48 unsigned int protoff,
49 int *hotdrop)
50{
51 struct ip_auth_hdr *ah = NULL, _ah;
52 const struct ip6t_ah *ahinfo = matchinfo;
53 unsigned int temp;
54 int len;
55 u8 nexthdr;
56 unsigned int ptr;
57 unsigned int hdrlen = 0;
58
59 /*DEBUGP("IPv6 AH entered\n");*/
60 /* if (opt->auth == 0) return 0;
61 * It does not filled on output */
62
63 /* type of the 1st exthdr */
64 nexthdr = skb->nh.ipv6h->nexthdr;
65 /* pointer to the 1st exthdr */
66 ptr = sizeof(struct ipv6hdr);
67 /* available length */
68 len = skb->len - ptr;
69 temp = 0;
70
71 while (ip6t_ext_hdr(nexthdr)) {
72 struct ipv6_opt_hdr _hdr, *hp;
73
74 DEBUGP("ipv6_ah header iteration \n");
75
76 /* Is there enough space for the next ext header? */
77 if (len < sizeof(struct ipv6_opt_hdr))
78 return 0;
79 /* No more exthdr -> evaluate */
80 if (nexthdr == NEXTHDR_NONE)
81 break;
82 /* ESP -> evaluate */
83 if (nexthdr == NEXTHDR_ESP)
84 break;
85
86 hp = skb_header_pointer(skb, ptr, sizeof(_hdr), &_hdr);
87 BUG_ON(hp == NULL);
88
89 /* Calculate the header length */
90 if (nexthdr == NEXTHDR_FRAGMENT)
91 hdrlen = 8;
92 else if (nexthdr == NEXTHDR_AUTH)
93 hdrlen = (hp->hdrlen+2)<<2;
94 else
95 hdrlen = ipv6_optlen(hp);
96
97 /* AH -> evaluate */
98 if (nexthdr == NEXTHDR_AUTH) {
99 temp |= MASK_AH;
100 break;
101 }
102
103
104 /* set the flag */
105 switch (nexthdr) {
106 case NEXTHDR_HOP:
107 case NEXTHDR_ROUTING:
108 case NEXTHDR_FRAGMENT:
109 case NEXTHDR_AUTH:
110 case NEXTHDR_DEST:
111 break;
112 default:
113 DEBUGP("ipv6_ah match: unknown nextheader %u\n",nexthdr);
114 return 0;
115 }
116
117 nexthdr = hp->nexthdr;
118 len -= hdrlen;
119 ptr += hdrlen;
120 if (ptr > skb->len) {
121 DEBUGP("ipv6_ah: new pointer too large! \n");
122 break;
123 }
124 }
125
126 /* AH header not found */
127 if (temp != MASK_AH)
128 return 0;
129
130 if (len < sizeof(struct ip_auth_hdr)){
131 *hotdrop = 1;
132 return 0;
133 }
134
135 ah = skb_header_pointer(skb, ptr, sizeof(_ah), &_ah);
136 BUG_ON(ah == NULL);
137
138 DEBUGP("IPv6 AH LEN %u %u ", hdrlen, ah->hdrlen);
139 DEBUGP("RES %04X ", ah->reserved);
140 DEBUGP("SPI %u %08X\n", ntohl(ah->spi), ntohl(ah->spi));
141
142 DEBUGP("IPv6 AH spi %02X ",
143 (spi_match(ahinfo->spis[0], ahinfo->spis[1],
144 ntohl(ah->spi),
145 !!(ahinfo->invflags & IP6T_AH_INV_SPI))));
146 DEBUGP("len %02X %04X %02X ",
147 ahinfo->hdrlen, hdrlen,
148 (!ahinfo->hdrlen ||
149 (ahinfo->hdrlen == hdrlen) ^
150 !!(ahinfo->invflags & IP6T_AH_INV_LEN)));
151 DEBUGP("res %02X %04X %02X\n",
152 ahinfo->hdrres, ah->reserved,
153 !(ahinfo->hdrres && ah->reserved));
154
155 return (ah != NULL)
156 &&
157 (spi_match(ahinfo->spis[0], ahinfo->spis[1],
158 ntohl(ah->spi),
159 !!(ahinfo->invflags & IP6T_AH_INV_SPI)))
160 &&
161 (!ahinfo->hdrlen ||
162 (ahinfo->hdrlen == hdrlen) ^
163 !!(ahinfo->invflags & IP6T_AH_INV_LEN))
164 &&
165 !(ahinfo->hdrres && ah->reserved);
166}
167
168/* Called when user tries to insert an entry of this type. */
169static int
170checkentry(const char *tablename,
171 const struct ip6t_ip6 *ip,
172 void *matchinfo,
173 unsigned int matchinfosize,
174 unsigned int hook_mask)
175{
176 const struct ip6t_ah *ahinfo = matchinfo;
177
178 if (matchinfosize != IP6T_ALIGN(sizeof(struct ip6t_ah))) {
179 DEBUGP("ip6t_ah: matchsize %u != %u\n",
180 matchinfosize, IP6T_ALIGN(sizeof(struct ip6t_ah)));
181 return 0;
182 }
183 if (ahinfo->invflags & ~IP6T_AH_INV_MASK) {
184 DEBUGP("ip6t_ah: unknown flags %X\n", ahinfo->invflags);
185 return 0;
186 }
187 return 1;
188}
189
190static struct ip6t_match ah_match = {
191 .name = "ah",
192 .match = &match,
193 .checkentry = &checkentry,
194 .me = THIS_MODULE,
195};
196
197static int __init init(void)
198{
199 return ip6t_register_match(&ah_match);
200}
201
202static void __exit cleanup(void)
203{
204 ip6t_unregister_match(&ah_match);
205}
206
207module_init(init);
208module_exit(cleanup);