1/* IP tables module for matching IPsec policy 2 * 3 * Copyright (c) 2004,2005 Patrick McHardy, <kaber@trash.net> 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/kernel.h> 11#include <linux/module.h> 12#include <linux/skbuff.h> 13#include <linux/init.h> 14#include <net/xfrm.h> 15 16#include <linux/netfilter/xt_policy.h> 17#include <linux/netfilter/x_tables.h> 18 19MODULE_AUTHOR("Patrick McHardy <kaber@trash.net>"); 20MODULE_DESCRIPTION("Xtables IPsec policy matching module"); 21MODULE_LICENSE("GPL"); 22 23static inline int 24xt_addr_cmp(const union xt_policy_addr *a1, const union xt_policy_addr *m, 25 const union xt_policy_addr *a2, unsigned short family) 26{ 27 switch (family) { 28 case AF_INET: 29 return !((a1->a4.s_addr ^ a2->a4.s_addr) & m->a4.s_addr); 30 case AF_INET6: 31 return !ipv6_masked_addr_cmp(&a1->a6, &m->a6, &a2->a6); 32 } 33 return 0; 34} 35 36static inline int 37match_xfrm_state(struct xfrm_state *x, const struct xt_policy_elem *e, 38 unsigned short family) 39{ 40#define MATCH_ADDR(x,y,z) (!e->match.x || \ 41 (xt_addr_cmp(&e->x, &e->y, z, family) \ 42 ^ e->invert.x)) 43#define MATCH(x,y) (!e->match.x || ((e->x == (y)) ^ e->invert.x)) 44 45 return MATCH_ADDR(saddr, smask, (union xt_policy_addr *)&x->props.saddr) && 46 MATCH_ADDR(daddr, dmask, (union xt_policy_addr *)&x->id.daddr) && 47 MATCH(proto, x->id.proto) && 48 MATCH(mode, x->props.mode) && 49 MATCH(spi, x->id.spi) && 50 MATCH(reqid, x->props.reqid); 51} 52 53static int 54match_policy_in(const struct sk_buff *skb, const struct xt_policy_info *info, 55 unsigned short family) 56{ 57 const struct xt_policy_elem *e; 58 struct sec_path *sp = skb->sp; 59 int strict = info->flags & XT_POLICY_MATCH_STRICT; 60 int i, pos; 61 62 if (sp == NULL) 63 return -1; 64 if (strict && info->len != sp->len) 65 return 0; 66 67 for (i = sp->len - 1; i >= 0; i--) { 68 pos = strict ? i - sp->len + 1 : 0; 69 if (pos >= info->len) 70 return 0; 71 e = &info->pol[pos]; 72 73 if (match_xfrm_state(sp->xvec[i], e, family)) { 74 if (!strict) 75 return 1; 76 } else if (strict) 77 return 0; 78 } 79 80 return strict ? 1 : 0; 81} 82 83static int 84match_policy_out(const struct sk_buff *skb, const struct xt_policy_info *info, 85 unsigned short family) 86{ 87 const struct xt_policy_elem *e; 88 struct dst_entry *dst = skb->dst; 89 int strict = info->flags & XT_POLICY_MATCH_STRICT; 90 int i, pos; 91 92 if (dst->xfrm == NULL) 93 return -1; 94 95 for (i = 0; dst && dst->xfrm; dst = dst->child, i++) { 96 pos = strict ? i : 0; 97 if (pos >= info->len) 98 return 0; 99 e = &info->pol[pos]; 100 101 if (match_xfrm_state(dst->xfrm, e, family)) { 102 if (!strict) 103 return 1; 104 } else if (strict) 105 return 0; 106 } 107 108 return strict ? i == info->len : 0; 109} 110 111static int match(const struct sk_buff *skb, 112 const struct net_device *in, 113 const struct net_device *out, 114 const struct xt_match *match, 115 const void *matchinfo, 116 int offset, 117 unsigned int protoff, 118 int *hotdrop) 119{ 120 const struct xt_policy_info *info = matchinfo; 121 int ret; 122 123 if (info->flags & XT_POLICY_MATCH_IN) 124 ret = match_policy_in(skb, info, match->family); 125 else 126 ret = match_policy_out(skb, info, match->family); 127 128 if (ret < 0) 129 ret = info->flags & XT_POLICY_MATCH_NONE ? 1 : 0; 130 else if (info->flags & XT_POLICY_MATCH_NONE) 131 ret = 0; 132 133 return ret; 134} 135 136static int checkentry(const char *tablename, const void *ip_void, 137 const struct xt_match *match, 138 void *matchinfo, unsigned int hook_mask) 139{ 140 struct xt_policy_info *info = matchinfo; 141 142 if (!(info->flags & (XT_POLICY_MATCH_IN|XT_POLICY_MATCH_OUT))) { 143 printk(KERN_ERR "xt_policy: neither incoming nor " 144 "outgoing policy selected\n"); 145 return 0; 146 } 147 /* hook values are equal for IPv4 and IPv6 */ 148 if (hook_mask & (1 << NF_IP_PRE_ROUTING | 1 << NF_IP_LOCAL_IN) 149 && info->flags & XT_POLICY_MATCH_OUT) { 150 printk(KERN_ERR "xt_policy: output policy not valid in " 151 "PRE_ROUTING and INPUT\n"); 152 return 0; 153 } 154 if (hook_mask & (1 << NF_IP_POST_ROUTING | 1 << NF_IP_LOCAL_OUT) 155 && info->flags & XT_POLICY_MATCH_IN) { 156 printk(KERN_ERR "xt_policy: input policy not valid in " 157 "POST_ROUTING and OUTPUT\n"); 158 return 0; 159 } 160 if (info->len > XT_POLICY_MAX_ELEM) { 161 printk(KERN_ERR "xt_policy: too many policy elements\n"); 162 return 0; 163 } 164 return 1; 165} 166 167static struct xt_match xt_policy_match[] = { 168 { 169 .name = "policy", 170 .family = AF_INET, 171 .checkentry = checkentry, 172 .match = match, 173 .matchsize = sizeof(struct xt_policy_info), 174 .me = THIS_MODULE, 175 }, 176 { 177 .name = "policy", 178 .family = AF_INET6, 179 .checkentry = checkentry, 180 .match = match, 181 .matchsize = sizeof(struct xt_policy_info), 182 .me = THIS_MODULE, 183 }, 184}; 185 186static int __init init(void) 187{ 188 return xt_register_matches(xt_policy_match, 189 ARRAY_SIZE(xt_policy_match)); 190} 191 192static void __exit fini(void) 193{ 194 xt_unregister_matches(xt_policy_match, ARRAY_SIZE(xt_policy_match)); 195} 196 197module_init(init); 198module_exit(fini); 199MODULE_ALIAS("ipt_policy"); 200MODULE_ALIAS("ip6t_policy"); 201