Linux Kernel  3.7.1
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
ip6t_hbh.c
Go to the documentation of this file.
1 /* Kernel module to match Hop-by-Hop and Destination parameters. */
2 
3 /* (C) 2001-2002 Andras Kis-Szabo <[email protected]>
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 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
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 <asm/byteorder.h>
18 
19 #include <linux/netfilter/x_tables.h>
20 #include <linux/netfilter_ipv6/ip6_tables.h>
22 
23 MODULE_LICENSE("GPL");
24 MODULE_DESCRIPTION("Xtables: IPv6 Hop-By-Hop and Destination Header match");
25 MODULE_AUTHOR("Andras Kis-Szabo <[email protected]>");
26 MODULE_ALIAS("ip6t_dst");
27 
28 /*
29  * (Type & 0xC0) >> 6
30  * 0 -> ignorable
31  * 1 -> must drop the packet
32  * 2 -> send ICMP PARM PROB regardless and drop packet
33  * 3 -> Send ICMP if not a multicast address and drop packet
34  * (Type & 0x20) >> 5
35  * 0 -> invariant
36  * 1 -> can change the routing
37  * (Type & 0x1F) Type
38  * 0 -> Pad1 (only 1 byte!)
39  * 1 -> PadN LENGTH info (total length = length + 2)
40  * C0 | 2 -> JUMBO 4 x x x x ( xxxx > 64k )
41  * 5 -> RTALERT 2 x x
42  */
43 
44 static struct xt_match hbh_mt6_reg[] __read_mostly;
45 
46 static bool
47 hbh_mt6(const struct sk_buff *skb, struct xt_action_param *par)
48 {
49  struct ipv6_opt_hdr _optsh;
50  const struct ipv6_opt_hdr *oh;
51  const struct ip6t_opts *optinfo = par->matchinfo;
52  unsigned int temp;
53  unsigned int ptr = 0;
54  unsigned int hdrlen = 0;
55  bool ret = false;
56  u8 _opttype;
57  u8 _optlen;
58  const u_int8_t *tp = NULL;
59  const u_int8_t *lp = NULL;
60  unsigned int optlen;
61  int err;
62 
63  err = ipv6_find_hdr(skb, &ptr,
64  (par->match == &hbh_mt6_reg[0]) ?
66  if (err < 0) {
67  if (err != -ENOENT)
68  par->hotdrop = true;
69  return false;
70  }
71 
72  oh = skb_header_pointer(skb, ptr, sizeof(_optsh), &_optsh);
73  if (oh == NULL) {
74  par->hotdrop = true;
75  return false;
76  }
77 
78  hdrlen = ipv6_optlen(oh);
79  if (skb->len - ptr < hdrlen) {
80  /* Packet smaller than it's length field */
81  return false;
82  }
83 
84  pr_debug("IPv6 OPTS LEN %u %u ", hdrlen, oh->hdrlen);
85 
86  pr_debug("len %02X %04X %02X ",
87  optinfo->hdrlen, hdrlen,
88  (!(optinfo->flags & IP6T_OPTS_LEN) ||
89  ((optinfo->hdrlen == hdrlen) ^
90  !!(optinfo->invflags & IP6T_OPTS_INV_LEN))));
91 
92  ret = (oh != NULL) &&
93  (!(optinfo->flags & IP6T_OPTS_LEN) ||
94  ((optinfo->hdrlen == hdrlen) ^
95  !!(optinfo->invflags & IP6T_OPTS_INV_LEN)));
96 
97  ptr += 2;
98  hdrlen -= 2;
99  if (!(optinfo->flags & IP6T_OPTS_OPTS)) {
100  return ret;
101  } else {
102  pr_debug("Strict ");
103  pr_debug("#%d ", optinfo->optsnr);
104  for (temp = 0; temp < optinfo->optsnr; temp++) {
105  /* type field exists ? */
106  if (hdrlen < 1)
107  break;
108  tp = skb_header_pointer(skb, ptr, sizeof(_opttype),
109  &_opttype);
110  if (tp == NULL)
111  break;
112 
113  /* Type check */
114  if (*tp != (optinfo->opts[temp] & 0xFF00) >> 8) {
115  pr_debug("Tbad %02X %02X\n", *tp,
116  (optinfo->opts[temp] & 0xFF00) >> 8);
117  return false;
118  } else {
119  pr_debug("Tok ");
120  }
121  /* Length check */
122  if (*tp) {
123  u16 spec_len;
124 
125  /* length field exists ? */
126  if (hdrlen < 2)
127  break;
128  lp = skb_header_pointer(skb, ptr + 1,
129  sizeof(_optlen),
130  &_optlen);
131  if (lp == NULL)
132  break;
133  spec_len = optinfo->opts[temp] & 0x00FF;
134 
135  if (spec_len != 0x00FF && spec_len != *lp) {
136  pr_debug("Lbad %02X %04X\n", *lp,
137  spec_len);
138  return false;
139  }
140  pr_debug("Lok ");
141  optlen = *lp + 2;
142  } else {
143  pr_debug("Pad1\n");
144  optlen = 1;
145  }
146 
147  /* Step to the next */
148  pr_debug("len%04X\n", optlen);
149 
150  if ((ptr > skb->len - optlen || hdrlen < optlen) &&
151  temp < optinfo->optsnr - 1) {
152  pr_debug("new pointer is too large!\n");
153  break;
154  }
155  ptr += optlen;
156  hdrlen -= optlen;
157  }
158  if (temp == optinfo->optsnr)
159  return ret;
160  else
161  return false;
162  }
163 
164  return false;
165 }
166 
167 static int hbh_mt6_check(const struct xt_mtchk_param *par)
168 {
169  const struct ip6t_opts *optsinfo = par->matchinfo;
170 
171  if (optsinfo->invflags & ~IP6T_OPTS_INV_MASK) {
172  pr_debug("unknown flags %X\n", optsinfo->invflags);
173  return -EINVAL;
174  }
175 
176  if (optsinfo->flags & IP6T_OPTS_NSTRICT) {
177  pr_debug("Not strict - not implemented");
178  return -EINVAL;
179  }
180 
181  return 0;
182 }
183 
184 static struct xt_match hbh_mt6_reg[] __read_mostly = {
185  {
186  /* Note, hbh_mt6 relies on the order of hbh_mt6_reg */
187  .name = "hbh",
188  .family = NFPROTO_IPV6,
189  .match = hbh_mt6,
190  .matchsize = sizeof(struct ip6t_opts),
191  .checkentry = hbh_mt6_check,
192  .me = THIS_MODULE,
193  },
194  {
195  .name = "dst",
196  .family = NFPROTO_IPV6,
197  .match = hbh_mt6,
198  .matchsize = sizeof(struct ip6t_opts),
199  .checkentry = hbh_mt6_check,
200  .me = THIS_MODULE,
201  },
202 };
203 
204 static int __init hbh_mt6_init(void)
205 {
206  return xt_register_matches(hbh_mt6_reg, ARRAY_SIZE(hbh_mt6_reg));
207 }
208 
209 static void __exit hbh_mt6_exit(void)
210 {
211  xt_unregister_matches(hbh_mt6_reg, ARRAY_SIZE(hbh_mt6_reg));
212 }
213 
214 module_init(hbh_mt6_init);
215 module_exit(hbh_mt6_exit);