1 /* 2 * net/ipv6/fib6_rules.c IPv6 Routing Policy Rules 3 * 4 * Copyright (C)2003-2006 Helsinki University of Technology 5 * Copyright (C)2003-2006 USAGI/WIDE Project 6 * 7 * This program is free software; you can redistribute it and/or 8 * modify it under the terms of the GNU General Public License as 9 * published by the Free Software Foundation, version 2. 10 * 11 * Authors 12 * Thomas Graf <tgraf@suug.ch> 13 * Ville Nuorvala <vnuorval@tcs.hut.fi> 14 */ 15 16 #include <linux/netdevice.h> 17 #include <linux/export.h> 18 19 #include <net/fib_rules.h> 20 #include <net/ipv6.h> 21 #include <net/addrconf.h> 22 #include <net/ip6_route.h> 23 #include <net/netlink.h> 24 25 struct fib6_rule 26 { 27 struct fib_rule common; 28 struct rt6key src; 29 struct rt6key dst; 30 u8 tclass; 31 }; 32 33 struct dst_entry *fib6_rule_lookup(struct net *net, struct flowi6 *fl6, 34 int flags, pol_lookup_t lookup) 35 { 36 struct fib_lookup_arg arg = { 37 .lookup_ptr = lookup, 38 .flags = FIB_LOOKUP_NOREF, 39 }; 40 41 fib_rules_lookup(net->ipv6.fib6_rules_ops, 42 flowi6_to_flowi(fl6), flags, &arg); 43 44 if (arg.result) 45 return arg.result; 46 47 dst_hold(&net->ipv6.ip6_null_entry->dst); 48 return &net->ipv6.ip6_null_entry->dst; 49 } 50 51 static int fib6_rule_action(struct fib_rule *rule, struct flowi *flp, 52 int flags, struct fib_lookup_arg *arg) 53 { 54 struct flowi6 *flp6 = &flp->u.ip6; 55 struct rt6_info *rt = NULL; 56 struct fib6_table *table; 57 struct net *net = rule->fr_net; 58 pol_lookup_t lookup = arg->lookup_ptr; 59 60 switch (rule->action) { 61 case FR_ACT_TO_TBL: 62 break; 63 case FR_ACT_UNREACHABLE: 64 rt = net->ipv6.ip6_null_entry; 65 goto discard_pkt; 66 default: 67 case FR_ACT_BLACKHOLE: 68 rt = net->ipv6.ip6_blk_hole_entry; 69 goto discard_pkt; 70 case FR_ACT_PROHIBIT: 71 rt = net->ipv6.ip6_prohibit_entry; 72 goto discard_pkt; 73 } 74 75 table = fib6_get_table(net, rule->table); 76 if (table) 77 rt = lookup(net, table, flp6, flags); 78 79 if (rt != net->ipv6.ip6_null_entry) { 80 struct fib6_rule *r = (struct fib6_rule *)rule; 81 82 /* 83 * If we need to find a source address for this traffic, 84 * we check the result if it meets requirement of the rule. 85 */ 86 if ((rule->flags & FIB_RULE_FIND_SADDR) && 87 r->src.plen && !(flags & RT6_LOOKUP_F_HAS_SADDR)) { 88 struct in6_addr saddr; 89 90 if (ipv6_dev_get_saddr(net, 91 ip6_dst_idev(&rt->dst)->dev, 92 &flp6->daddr, 93 rt6_flags2srcprefs(flags), 94 &saddr)) 95 goto again; 96 if (!ipv6_prefix_equal(&saddr, &r->src.addr, 97 r->src.plen)) 98 goto again; 99 flp6->saddr = saddr; 100 } 101 goto out; 102 } 103 again: 104 dst_release(&rt->dst); 105 rt = NULL; 106 goto out; 107 108 discard_pkt: 109 dst_hold(&rt->dst); 110 out: 111 arg->result = rt; 112 return rt == NULL ? -EAGAIN : 0; 113 } 114 115 116 static int fib6_rule_match(struct fib_rule *rule, struct flowi *fl, int flags) 117 { 118 struct fib6_rule *r = (struct fib6_rule *) rule; 119 struct flowi6 *fl6 = &fl->u.ip6; 120 121 if (r->dst.plen && 122 !ipv6_prefix_equal(&fl6->daddr, &r->dst.addr, r->dst.plen)) 123 return 0; 124 125 /* 126 * If FIB_RULE_FIND_SADDR is set and we do not have a 127 * source address for the traffic, we defer check for 128 * source address. 129 */ 130 if (r->src.plen) { 131 if (flags & RT6_LOOKUP_F_HAS_SADDR) { 132 if (!ipv6_prefix_equal(&fl6->saddr, &r->src.addr, 133 r->src.plen)) 134 return 0; 135 } else if (!(r->common.flags & FIB_RULE_FIND_SADDR)) 136 return 0; 137 } 138 139 if (r->tclass && r->tclass != ((ntohl(fl6->flowlabel) >> 20) & 0xff)) 140 return 0; 141 142 return 1; 143 } 144 145 static const struct nla_policy fib6_rule_policy[FRA_MAX+1] = { 146 FRA_GENERIC_POLICY, 147 }; 148 149 static int fib6_rule_configure(struct fib_rule *rule, struct sk_buff *skb, 150 struct fib_rule_hdr *frh, 151 struct nlattr **tb) 152 { 153 int err = -EINVAL; 154 struct net *net = sock_net(skb->sk); 155 struct fib6_rule *rule6 = (struct fib6_rule *) rule; 156 157 if (rule->action == FR_ACT_TO_TBL) { 158 if (rule->table == RT6_TABLE_UNSPEC) 159 goto errout; 160 161 if (fib6_new_table(net, rule->table) == NULL) { 162 err = -ENOBUFS; 163 goto errout; 164 } 165 } 166 167 if (frh->src_len) 168 nla_memcpy(&rule6->src.addr, tb[FRA_SRC], 169 sizeof(struct in6_addr)); 170 171 if (frh->dst_len) 172 nla_memcpy(&rule6->dst.addr, tb[FRA_DST], 173 sizeof(struct in6_addr)); 174 175 rule6->src.plen = frh->src_len; 176 rule6->dst.plen = frh->dst_len; 177 rule6->tclass = frh->tos; 178 179 err = 0; 180 errout: 181 return err; 182 } 183 184 static int fib6_rule_compare(struct fib_rule *rule, struct fib_rule_hdr *frh, 185 struct nlattr **tb) 186 { 187 struct fib6_rule *rule6 = (struct fib6_rule *) rule; 188 189 if (frh->src_len && (rule6->src.plen != frh->src_len)) 190 return 0; 191 192 if (frh->dst_len && (rule6->dst.plen != frh->dst_len)) 193 return 0; 194 195 if (frh->tos && (rule6->tclass != frh->tos)) 196 return 0; 197 198 if (frh->src_len && 199 nla_memcmp(tb[FRA_SRC], &rule6->src.addr, sizeof(struct in6_addr))) 200 return 0; 201 202 if (frh->dst_len && 203 nla_memcmp(tb[FRA_DST], &rule6->dst.addr, sizeof(struct in6_addr))) 204 return 0; 205 206 return 1; 207 } 208 209 static int fib6_rule_fill(struct fib_rule *rule, struct sk_buff *skb, 210 struct fib_rule_hdr *frh) 211 { 212 struct fib6_rule *rule6 = (struct fib6_rule *) rule; 213 214 frh->dst_len = rule6->dst.plen; 215 frh->src_len = rule6->src.plen; 216 frh->tos = rule6->tclass; 217 218 if (rule6->dst.plen) 219 NLA_PUT(skb, FRA_DST, sizeof(struct in6_addr), 220 &rule6->dst.addr); 221 222 if (rule6->src.plen) 223 NLA_PUT(skb, FRA_SRC, sizeof(struct in6_addr), 224 &rule6->src.addr); 225 226 return 0; 227 228 nla_put_failure: 229 return -ENOBUFS; 230 } 231 232 static u32 fib6_rule_default_pref(struct fib_rules_ops *ops) 233 { 234 return 0x3FFF; 235 } 236 237 static size_t fib6_rule_nlmsg_payload(struct fib_rule *rule) 238 { 239 return nla_total_size(16) /* dst */ 240 + nla_total_size(16); /* src */ 241 } 242 243 static const struct fib_rules_ops __net_initdata fib6_rules_ops_template = { 244 .family = AF_INET6, 245 .rule_size = sizeof(struct fib6_rule), 246 .addr_size = sizeof(struct in6_addr), 247 .action = fib6_rule_action, 248 .match = fib6_rule_match, 249 .configure = fib6_rule_configure, 250 .compare = fib6_rule_compare, 251 .fill = fib6_rule_fill, 252 .default_pref = fib6_rule_default_pref, 253 .nlmsg_payload = fib6_rule_nlmsg_payload, 254 .nlgroup = RTNLGRP_IPV6_RULE, 255 .policy = fib6_rule_policy, 256 .owner = THIS_MODULE, 257 .fro_net = &init_net, 258 }; 259 260 static int __net_init fib6_rules_net_init(struct net *net) 261 { 262 struct fib_rules_ops *ops; 263 int err = -ENOMEM; 264 265 ops = fib_rules_register(&fib6_rules_ops_template, net); 266 if (IS_ERR(ops)) 267 return PTR_ERR(ops); 268 net->ipv6.fib6_rules_ops = ops; 269 270 271 err = fib_default_rule_add(net->ipv6.fib6_rules_ops, 0, 272 RT6_TABLE_LOCAL, 0); 273 if (err) 274 goto out_fib6_rules_ops; 275 276 err = fib_default_rule_add(net->ipv6.fib6_rules_ops, 277 0x7FFE, RT6_TABLE_MAIN, 0); 278 if (err) 279 goto out_fib6_rules_ops; 280 281 out: 282 return err; 283 284 out_fib6_rules_ops: 285 fib_rules_unregister(ops); 286 goto out; 287 } 288 289 static void __net_exit fib6_rules_net_exit(struct net *net) 290 { 291 fib_rules_unregister(net->ipv6.fib6_rules_ops); 292 } 293 294 static struct pernet_operations fib6_rules_net_ops = { 295 .init = fib6_rules_net_init, 296 .exit = fib6_rules_net_exit, 297 }; 298 299 int __init fib6_rules_init(void) 300 { 301 return register_pernet_subsys(&fib6_rules_net_ops); 302 } 303 304 305 void fib6_rules_cleanup(void) 306 { 307 unregister_pernet_subsys(&fib6_rules_net_ops); 308 } 309