PageRenderTime 24ms CodeModel.GetById 13ms app.highlight 9ms RepoModel.GetById 0ms app.codeStats 0ms

/net/ipv6/netfilter/ip6t_dst.c

https://bitbucket.org/abioy/linux
C | 278 lines | 213 code | 34 blank | 31 comment | 53 complexity | 3b8dc9e4e1b7f5a0df1a8a3ad9e37c50 MD5 | raw file
Possible License(s): CC-BY-SA-3.0, GPL-2.0, LGPL-2.0, AGPL-1.0
  1/* Kernel module to match Hop-by-Hop and Destination parameters. */
  2#include <linux/module.h>
  3#include <linux/skbuff.h>
  4#include <linux/ipv6.h>
  5#include <linux/types.h>
  6#include <net/checksum.h>
  7#include <net/ipv6.h>
  8
  9#include <asm/byteorder.h>
 10
 11#include <linux/netfilter_ipv6/ip6_tables.h>
 12#include <linux/netfilter_ipv6/ip6t_opts.h>
 13
 14#define LOW(n)		(n & 0x00FF)
 15
 16#define HOPBYHOP	0
 17
 18MODULE_LICENSE("GPL");
 19#if HOPBYHOP
 20MODULE_DESCRIPTION("IPv6 HbH match");
 21#else
 22MODULE_DESCRIPTION("IPv6 DST match");
 23#endif
 24MODULE_AUTHOR("Andras Kis-Szabo <kisza@sch.bme.hu>");
 25
 26#if 0
 27#define DEBUGP printk
 28#else
 29#define DEBUGP(format, args...)
 30#endif
 31
 32/*
 33 * (Type & 0xC0) >> 6
 34 * 	0	-> ignorable
 35 * 	1	-> must drop the packet
 36 * 	2	-> send ICMP PARM PROB regardless and drop packet
 37 * 	3	-> Send ICMP if not a multicast address and drop packet
 38 *  (Type & 0x20) >> 5
 39 *  	0	-> invariant
 40 *  	1	-> can change the routing
 41 *  (Type & 0x1F) Type
 42 *      0	-> PAD0 (only 1 byte!)
 43 *      1	-> PAD1 LENGTH info (total length = length + 2)
 44 *      C0 | 2	-> JUMBO 4 x x x x ( xxxx > 64k )
 45 *      5	-> RTALERT 2 x x
 46 */
 47
 48static int
 49match(const struct sk_buff *skb,
 50      const struct net_device *in,
 51      const struct net_device *out,
 52      const void *matchinfo,
 53      int offset,
 54      const void *protohdr,
 55      u_int16_t datalen,
 56      int *hotdrop)
 57{
 58       struct ipv6_opt_hdr *optsh = NULL;
 59       const struct ip6t_opts *optinfo = matchinfo;
 60       unsigned int temp;
 61       unsigned int len;
 62       u8 nexthdr;
 63       unsigned int ptr;
 64       unsigned int hdrlen = 0;
 65       unsigned int ret = 0;
 66       u_int16_t *optdesc = NULL;
 67       
 68       /* type of the 1st exthdr */
 69       nexthdr = skb->nh.ipv6h->nexthdr;
 70       /* pointer to the 1st exthdr */
 71       ptr = sizeof(struct ipv6hdr);
 72       /* available length */
 73       len = skb->len - ptr;
 74       temp = 0;
 75
 76        while (ip6t_ext_hdr(nexthdr)) {
 77               struct ipv6_opt_hdr *hdr;
 78
 79              DEBUGP("ipv6_opts header iteration \n");
 80
 81              /* Is there enough space for the next ext header? */
 82                if (len < (int)sizeof(struct ipv6_opt_hdr))
 83                        return 0;
 84              /* No more exthdr -> evaluate */
 85                if (nexthdr == NEXTHDR_NONE) {
 86                     break;
 87              }
 88              /* ESP -> evaluate */
 89                if (nexthdr == NEXTHDR_ESP) {
 90                     break;
 91              }
 92
 93              hdr=(void *)(skb->data)+ptr;
 94
 95              /* Calculate the header length */
 96                if (nexthdr == NEXTHDR_FRAGMENT) {
 97                        hdrlen = 8;
 98                } else if (nexthdr == NEXTHDR_AUTH)
 99                        hdrlen = (hdr->hdrlen+2)<<2;
100                else
101                        hdrlen = ipv6_optlen(hdr);
102
103              /* OPTS -> evaluate */
104#if HOPBYHOP
105                if (nexthdr == NEXTHDR_HOP) {
106                     temp |= MASK_HOPOPTS;
107#else
108                if (nexthdr == NEXTHDR_DEST) {
109                     temp |= MASK_DSTOPTS;
110#endif
111                     break;
112              }
113
114
115              /* set the flag */
116              switch (nexthdr){
117                     case NEXTHDR_HOP:
118                     case NEXTHDR_ROUTING:
119                     case NEXTHDR_FRAGMENT:
120                     case NEXTHDR_AUTH:
121                     case NEXTHDR_DEST:
122                            break;
123                     default:
124                            DEBUGP("ipv6_opts match: unknown nextheader %u\n",nexthdr);
125                            return 0;
126                            break;
127              }
128
129                nexthdr = hdr->nexthdr;
130                len -= hdrlen;
131                ptr += hdrlen;
132		if ( ptr > skb->len ) {
133			DEBUGP("ipv6_opts: new pointer is too large! \n");
134			break;
135		}
136        }
137
138       /* OPTIONS header not found */
139#if HOPBYHOP
140       if ( temp != MASK_HOPOPTS ) return 0;
141#else
142       if ( temp != MASK_DSTOPTS ) return 0;
143#endif
144
145       if (len < (int)sizeof(struct ipv6_opt_hdr)){
146	       *hotdrop = 1;
147       		return 0;
148       }
149
150       if (len < hdrlen){
151	       /* Packet smaller than it's length field */
152       		return 0;
153       }
154
155       optsh=(void *)(skb->data)+ptr;
156
157       DEBUGP("IPv6 OPTS LEN %u %u ", hdrlen, optsh->hdrlen);
158
159       DEBUGP("len %02X %04X %02X ",
160       		optinfo->hdrlen, hdrlen,
161       		(!(optinfo->flags & IP6T_OPTS_LEN) ||
162                           ((optinfo->hdrlen == hdrlen) ^
163                           !!(optinfo->invflags & IP6T_OPTS_INV_LEN))));
164
165       ret = (optsh != NULL)
166       		&&
167	      	(!(optinfo->flags & IP6T_OPTS_LEN) ||
168                           ((optinfo->hdrlen == hdrlen) ^
169                           !!(optinfo->invflags & IP6T_OPTS_INV_LEN)));
170
171       temp = len = 0;
172       ptr += 2;
173       hdrlen -= 2;
174       if ( !(optinfo->flags & IP6T_OPTS_OPTS) ){
175	       return ret;
176	} else if (optinfo->flags & IP6T_OPTS_NSTRICT) {
177		DEBUGP("Not strict - not implemented");
178	} else {
179		DEBUGP("Strict ");
180		DEBUGP("#%d ",optinfo->optsnr);
181		for(temp=0; temp<optinfo->optsnr; temp++){
182			optdesc = (void *)(skb->data)+ptr;
183			/* Type check */
184			if ( (unsigned char)*optdesc != 
185				(optinfo->opts[temp] & 0xFF00)>>8 ){
186				DEBUGP("Tbad %02X %02X\n",
187						(unsigned char)*optdesc,
188						(optinfo->opts[temp] &
189						 0xFF00)>>8);
190				return 0;
191			} else {
192				DEBUGP("Tok ");
193			}
194			/* Length check */
195			if (((optinfo->opts[temp] & 0x00FF) != 0xFF) &&
196				(unsigned char)*optdesc != 0){
197				if ( ntohs((u16)*optdesc) != 
198						optinfo->opts[temp] ){
199					DEBUGP("Lbad %02X %04X %04X\n",
200							(unsigned char)*optdesc,
201							ntohs((u16)*optdesc),
202							optinfo->opts[temp]);
203					return 0;
204				} else {
205					DEBUGP("Lok ");
206				}
207			}
208			/* Step to the next */
209			if ((unsigned char)*optdesc == 0){
210				DEBUGP("PAD0 \n");
211				ptr++;
212				hdrlen--;
213			} else {
214				ptr += LOW(ntohs(*optdesc));
215				hdrlen -= LOW(ntohs(*optdesc));
216				DEBUGP("len%04X \n", 
217					LOW(ntohs(*optdesc)));
218			}
219			if (ptr > skb->len || ( !hdrlen && 
220				(temp != optinfo->optsnr - 1))) {
221				DEBUGP("new pointer is too large! \n");
222				break;
223			}
224		}
225		if (temp == optinfo->optsnr)
226			return ret;
227		else return 0;
228	}
229
230	return 0;
231}
232
233/* Called when user tries to insert an entry of this type. */
234static int
235checkentry(const char *tablename,
236          const struct ip6t_ip6 *ip,
237          void *matchinfo,
238          unsigned int matchinfosize,
239          unsigned int hook_mask)
240{
241       const struct ip6t_opts *optsinfo = matchinfo;
242
243       if (matchinfosize != IP6T_ALIGN(sizeof(struct ip6t_opts))) {
244              DEBUGP("ip6t_opts: matchsize %u != %u\n",
245                      matchinfosize, IP6T_ALIGN(sizeof(struct ip6t_opts)));
246              return 0;
247       }
248       if (optsinfo->invflags & ~IP6T_OPTS_INV_MASK) {
249              DEBUGP("ip6t_opts: unknown flags %X\n",
250                      optsinfo->invflags);
251              return 0;
252       }
253
254       return 1;
255}
256
257static struct ip6t_match opts_match = {
258#if HOPBYHOP
259	.name		= "hbh",
260#else
261	.name		= "dst",
262#endif
263	.match		= &match,
264	.checkentry	= &checkentry,
265};
266
267static int __init init(void)
268{
269       return ip6t_register_match(&opts_match);
270}
271
272static void __exit cleanup(void)
273{
274       ip6t_unregister_match(&opts_match);
275}
276
277module_init(init);
278module_exit(cleanup);