1/* 2 * IPv6 Firewall 3 * Linux INET6 implementation 4 * 5 * Authors: 6 * Pedro Roque <roque@di.fc.ul.pt> 7 * 8 * $Id: ip6_fw.c,v 1.1.1.1 2008/10/15 03:27:34 james26_jang Exp $ 9 * 10 * This program is free software; you can redistribute it and/or 11 * modify it under the terms of the GNU General Public License 12 * as published by the Free Software Foundation; either version 13 * 2 of the License, or (at your option) any later version. 14 */ 15 16#include <linux/config.h> 17#include <linux/errno.h> 18#include <linux/types.h> 19#include <linux/string.h> 20#include <linux/socket.h> 21#include <linux/sockios.h> 22#include <linux/net.h> 23#include <linux/route.h> 24#include <linux/netdevice.h> 25#include <linux/in6.h> 26#include <linux/udp.h> 27#include <linux/init.h> 28 29#include <net/ipv6.h> 30#include <net/ip6_route.h> 31#include <net/ip6_fw.h> 32#include <net/netlink.h> 33 34static unsigned long ip6_fw_rule_cnt; 35static struct ip6_fw_rule ip6_fw_rule_list = { 36 {0}, 37 NULL, NULL, 38 {0}, 39 IP6_FW_REJECT 40}; 41 42static int ip6_fw_accept(struct dst_entry *dst, struct fl_acc_args *args); 43 44struct flow_rule_ops ip6_fw_ops = { 45 ip6_fw_accept 46}; 47 48 49static struct rt6_info ip6_fw_null_entry = { 50 {{NULL, 0, 0, NULL, 51 0, 0, 0, 0, 0, 0, 0, 0, -ENETUNREACH, NULL, NULL, 52 ip6_pkt_discard, ip6_pkt_discard, NULL}}, 53 NULL, {{{0}}}, 256, RTF_REJECT|RTF_NONEXTHOP, ~0UL, 54 0, &ip6_fw_rule_list, {{{{0}}}, 128}, {{{{0}}}, 128} 55}; 56 57static struct fib6_node ip6_fw_fib = { 58 NULL, NULL, NULL, NULL, 59 &ip6_fw_null_entry, 60 0, RTN_ROOT|RTN_TL_ROOT, 0 61}; 62 63rwlock_t ip6_fw_lock = RW_LOCK_UNLOCKED; 64 65 66static void ip6_rule_add(struct ip6_fw_rule *rl) 67{ 68 struct ip6_fw_rule *next; 69 70 write_lock_bh(&ip6_fw_lock); 71 ip6_fw_rule_cnt++; 72 next = &ip6_fw_rule_list; 73 rl->next = next; 74 rl->prev = next->prev; 75 rl->prev->next = rl; 76 next->prev = rl; 77 write_unlock_bh(&ip6_fw_lock); 78} 79 80static void ip6_rule_del(struct ip6_fw_rule *rl) 81{ 82 struct ip6_fw_rule *next, *prev; 83 84 write_lock_bh(&ip6_fw_lock); 85 ip6_fw_rule_cnt--; 86 next = rl->next; 87 prev = rl->prev; 88 next->prev = prev; 89 prev->next = next; 90 write_unlock_bh(&ip6_fw_lock); 91} 92 93static __inline__ struct ip6_fw_rule * ip6_fwrule_alloc(void) 94{ 95 struct ip6_fw_rule *rl; 96 97 rl = kmalloc(sizeof(struct ip6_fw_rule), GFP_ATOMIC); 98 if (rl) 99 { 100 memset(rl, 0, sizeof(struct ip6_fw_rule)); 101 rl->flowr.ops = &ip6_fw_ops; 102 } 103 return rl; 104} 105 106static __inline__ void ip6_fwrule_free(struct ip6_fw_rule * rl) 107{ 108 kfree(rl); 109} 110 111static __inline__ int port_match(int rl_port, int fl_port) 112{ 113 int res = 0; 114 if (rl_port == 0 || (rl_port == fl_port)) 115 res = 1; 116 return res; 117} 118 119static int ip6_fw_accept_trans(struct ip6_fw_rule *rl, 120 struct fl_acc_args *args) 121{ 122 int res = FLOWR_NODECISION; 123 int proto = 0; 124 int sport = 0; 125 int dport = 0; 126 127 switch (args->type) { 128 case FL_ARG_FORWARD: 129 { 130 struct sk_buff *skb = args->fl_u.skb; 131 struct ipv6hdr *hdr = skb->nh.ipv6h; 132 int len; 133 134 len = skb->len - sizeof(struct ipv6hdr); 135 136 proto = hdr->nexthdr; 137 138 switch (proto) { 139 case IPPROTO_TCP: 140 { 141 struct tcphdr *th; 142 143 if (len < sizeof(struct tcphdr)) { 144 res = FLOWR_ERROR; 145 goto out; 146 } 147 th = (struct tcphdr *)(hdr + 1); 148 sport = th->source; 149 dport = th->dest; 150 break; 151 } 152 case IPPROTO_UDP: 153 { 154 struct udphdr *uh; 155 156 if (len < sizeof(struct udphdr)) { 157 res = FLOWR_ERROR; 158 goto out; 159 } 160 uh = (struct udphdr *)(hdr + 1); 161 sport = uh->source; 162 dport = uh->dest; 163 break; 164 } 165 default: 166 goto out; 167 }; 168 break; 169 } 170 171 case FL_ARG_ORIGIN: 172 { 173 proto = args->fl_u.fl_o.flow->proto; 174 175 if (proto == IPPROTO_ICMPV6) { 176 goto out; 177 } else { 178 sport = args->fl_u.fl_o.flow->uli_u.ports.sport; 179 dport = args->fl_u.fl_o.flow->uli_u.ports.dport; 180 } 181 break; 182 } 183 184 if (proto == rl->info.proto && 185 port_match(args->fl_u.fl_o.flow->uli_u.ports.sport, sport) && 186 port_match(args->fl_u.fl_o.flow->uli_u.ports.dport, dport)) { 187 if (rl->policy & IP6_FW_REJECT) 188 res = FLOWR_SELECT; 189 else 190 res = FLOWR_CLEAR; 191 } 192 193 default: 194#if IP6_FW_DEBUG >= 1 195 printk(KERN_DEBUG "ip6_fw_accept: unknown arg type\n"); 196#endif 197 goto out; 198 }; 199 200out: 201 return res; 202} 203 204static int ip6_fw_accept(struct dst_entry *dst, struct fl_acc_args *args) 205{ 206 struct rt6_info *rt; 207 struct ip6_fw_rule *rl; 208 int proto; 209 int res = FLOWR_NODECISION; 210 211 rt = (struct rt6_info *) dst; 212 rl = (struct ip6_fw_rule *) rt->rt6i_flowr; 213 214 proto = rl->info.proto; 215 216 switch (proto) { 217 case 0: 218 if (rl->policy & IP6_FW_REJECT) 219 res = FLOWR_SELECT; 220 else 221 res = FLOWR_CLEAR; 222 break; 223 case IPPROTO_TCP: 224 case IPPROTO_UDP: 225 res = ip6_fw_accept_trans(rl, args); 226 break; 227 case IPPROTO_ICMPV6: 228 }; 229 230 return res; 231} 232 233static struct dst_entry * ip6_fw_dup(struct dst_entry *frule, 234 struct dst_entry *rt, 235 struct fl_acc_args *args) 236{ 237 struct ip6_fw_rule *rl; 238 struct rt6_info *nrt; 239 struct rt6_info *frt; 240 241 frt = (struct rt6_info *) frule; 242 243 rl = (struct ip6_fw_rule *) frt->rt6i_flowr; 244 245 nrt = ip6_rt_copy((struct rt6_info *) rt); 246 247 if (nrt) { 248 nrt->u.dst.input = frule->input; 249 nrt->u.dst.output = frule->output; 250 251 nrt->rt6i_flowr = flow_clone(frt->rt6i_flowr); 252 253 nrt->rt6i_flags |= RTF_CACHE; 254 nrt->rt6i_tstamp = jiffies; 255 } 256 257 return (struct dst_entry *) nrt; 258} 259 260int ip6_fw_reject(struct sk_buff *skb) 261{ 262#if IP6_FW_DEBUG >= 1 263 printk(KERN_DEBUG "packet rejected: \n"); 264#endif 265 266 icmpv6_send(skb, ICMPV6_DEST_UNREACH, ICMPV6_ADM_PROHIBITED, 0, 267 skb->dev); 268 /* 269 * send it via netlink, as (rule, skb) 270 */ 271 272 kfree_skb(skb); 273 return 0; 274} 275 276int ip6_fw_discard(struct sk_buff *skb) 277{ 278 printk(KERN_DEBUG "ip6_fw: BUG fw_reject called\n"); 279 kfree_skb(skb); 280 return 0; 281} 282 283int ip6_fw_msg_add(struct ip6_fw_msg *msg) 284{ 285 struct in6_rtmsg rtmsg; 286 struct ip6_fw_rule *rl; 287 struct rt6_info *rt; 288 int err; 289 290 ipv6_addr_copy(&rtmsg.rtmsg_dst, &msg->dst); 291 ipv6_addr_copy(&rtmsg.rtmsg_src, &msg->src); 292 rtmsg.rtmsg_dst_len = msg->dst_len; 293 rtmsg.rtmsg_src_len = msg->src_len; 294 rtmsg.rtmsg_metric = IP6_RT_PRIO_FW; 295 296 rl = ip6_fwrule_alloc(); 297 298 if (rl == NULL) 299 return -ENOMEM; 300 301 rl->policy = msg->policy; 302 rl->info.proto = msg->proto; 303 rl->info.uli_u.data = msg->u.data; 304 305 rtmsg.rtmsg_flags = RTF_NONEXTHOP|RTF_POLICY; 306 err = ip6_route_add(&rtmsg); 307 308 if (err) { 309 ip6_fwrule_free(rl); 310 return err; 311 } 312 313 /* The rest will not work for now. --ABK (989725) */ 314 315#ifndef notdef 316 ip6_fwrule_free(rl); 317 return -EPERM; 318#else 319 rt->u.dst.error = -EPERM; 320 321 if (msg->policy == IP6_FW_ACCEPT) { 322 /* 323 * Accept rules are never selected 324 * (i.e. packets use normal forwarding) 325 */ 326 rt->u.dst.input = ip6_fw_discard; 327 rt->u.dst.output = ip6_fw_discard; 328 } else { 329 rt->u.dst.input = ip6_fw_reject; 330 rt->u.dst.output = ip6_fw_reject; 331 } 332 333 ip6_rule_add(rl); 334 335 rt->rt6i_flowr = flow_clone((struct flow_rule *)rl); 336 337 return 0; 338#endif 339} 340 341static int ip6_fw_msgrcv(int unit, struct sk_buff *skb) 342{ 343 int count = 0; 344 345 while (skb->len) { 346 struct ip6_fw_msg *msg; 347 348 if (skb->len < sizeof(struct ip6_fw_msg)) { 349 count = -EINVAL; 350 break; 351 } 352 353 msg = (struct ip6_fw_msg *) skb->data; 354 skb_pull(skb, sizeof(struct ip6_fw_msg)); 355 count += sizeof(struct ip6_fw_msg); 356 357 switch (msg->action) { 358 case IP6_FW_MSG_ADD: 359 ip6_fw_msg_add(msg); 360 break; 361 case IP6_FW_MSG_DEL: 362 break; 363 default: 364 return -EINVAL; 365 }; 366 } 367 368 return count; 369} 370 371static void ip6_fw_destroy(struct flow_rule *rl) 372{ 373 ip6_fwrule_free((struct ip6_fw_rule *)rl); 374} 375 376#ifdef MODULE 377#define ip6_fw_init module_init 378#endif 379 380void __init ip6_fw_init(void) 381{ 382 netlink_attach(NETLINK_IP6_FW, ip6_fw_msgrcv); 383} 384 385#ifdef MODULE 386void cleanup_module(void) 387{ 388 netlink_detach(NETLINK_IP6_FW); 389} 390#endif 391