at v2.6.19-rc3 316 lines 7.0 kB view raw
1 2/* 3 * DECnet An implementation of the DECnet protocol suite for the LINUX 4 * operating system. DECnet is implemented using the BSD Socket 5 * interface as the means of communication with the user level. 6 * 7 * DECnet Routing Forwarding Information Base (Rules) 8 * 9 * Author: Steve Whitehouse <SteveW@ACM.org> 10 * Mostly copied from Alexey Kuznetsov's ipv4/fib_rules.c 11 * 12 * 13 * Changes: 14 * Steve Whitehouse <steve@chygwyn.com> 15 * Updated for Thomas Graf's generic rules 16 * 17 */ 18#include <linux/net.h> 19#include <linux/init.h> 20#include <linux/netlink.h> 21#include <linux/rtnetlink.h> 22#include <linux/netdevice.h> 23#include <linux/spinlock.h> 24#include <linux/list.h> 25#include <linux/rcupdate.h> 26#include <net/neighbour.h> 27#include <net/dst.h> 28#include <net/flow.h> 29#include <net/fib_rules.h> 30#include <net/dn.h> 31#include <net/dn_fib.h> 32#include <net/dn_neigh.h> 33#include <net/dn_dev.h> 34 35static struct fib_rules_ops dn_fib_rules_ops; 36 37struct dn_fib_rule 38{ 39 struct fib_rule common; 40 unsigned char dst_len; 41 unsigned char src_len; 42 __le16 src; 43 __le16 srcmask; 44 __le16 dst; 45 __le16 dstmask; 46 __le16 srcmap; 47 u8 flags; 48#ifdef CONFIG_DECNET_ROUTE_FWMARK 49 u32 fwmark; 50 u32 fwmask; 51#endif 52}; 53 54static struct dn_fib_rule default_rule = { 55 .common = { 56 .refcnt = ATOMIC_INIT(2), 57 .pref = 0x7fff, 58 .table = RT_TABLE_MAIN, 59 .action = FR_ACT_TO_TBL, 60 }, 61}; 62 63static LIST_HEAD(dn_fib_rules); 64 65 66int dn_fib_lookup(struct flowi *flp, struct dn_fib_res *res) 67{ 68 struct fib_lookup_arg arg = { 69 .result = res, 70 }; 71 int err; 72 73 err = fib_rules_lookup(&dn_fib_rules_ops, flp, 0, &arg); 74 res->r = arg.rule; 75 76 return err; 77} 78 79static int dn_fib_rule_action(struct fib_rule *rule, struct flowi *flp, 80 int flags, struct fib_lookup_arg *arg) 81{ 82 int err = -EAGAIN; 83 struct dn_fib_table *tbl; 84 85 switch(rule->action) { 86 case FR_ACT_TO_TBL: 87 break; 88 89 case FR_ACT_UNREACHABLE: 90 err = -ENETUNREACH; 91 goto errout; 92 93 case FR_ACT_PROHIBIT: 94 err = -EACCES; 95 goto errout; 96 97 case FR_ACT_BLACKHOLE: 98 default: 99 err = -EINVAL; 100 goto errout; 101 } 102 103 tbl = dn_fib_get_table(rule->table, 0); 104 if (tbl == NULL) 105 goto errout; 106 107 err = tbl->lookup(tbl, flp, (struct dn_fib_res *)arg->result); 108 if (err > 0) 109 err = -EAGAIN; 110errout: 111 return err; 112} 113 114static struct nla_policy dn_fib_rule_policy[FRA_MAX+1] __read_mostly = { 115 [FRA_IFNAME] = { .type = NLA_STRING, .len = IFNAMSIZ - 1 }, 116 [FRA_PRIORITY] = { .type = NLA_U32 }, 117 [FRA_SRC] = { .type = NLA_U16 }, 118 [FRA_DST] = { .type = NLA_U16 }, 119 [FRA_FWMARK] = { .type = NLA_U32 }, 120 [FRA_FWMASK] = { .type = NLA_U32 }, 121 [FRA_TABLE] = { .type = NLA_U32 }, 122}; 123 124static int dn_fib_rule_match(struct fib_rule *rule, struct flowi *fl, int flags) 125{ 126 struct dn_fib_rule *r = (struct dn_fib_rule *)rule; 127 u16 daddr = fl->fld_dst; 128 u16 saddr = fl->fld_src; 129 130 if (((saddr ^ r->src) & r->srcmask) || 131 ((daddr ^ r->dst) & r->dstmask)) 132 return 0; 133 134#ifdef CONFIG_DECNET_ROUTE_FWMARK 135 if ((r->fwmark ^ fl->fld_fwmark) & r->fwmask) 136 return 0; 137#endif 138 139 return 1; 140} 141 142static int dn_fib_rule_configure(struct fib_rule *rule, struct sk_buff *skb, 143 struct nlmsghdr *nlh, struct fib_rule_hdr *frh, 144 struct nlattr **tb) 145{ 146 int err = -EINVAL; 147 struct dn_fib_rule *r = (struct dn_fib_rule *)rule; 148 149 if (frh->src_len > 16 || frh->dst_len > 16 || frh->tos) 150 goto errout; 151 152 if (rule->table == RT_TABLE_UNSPEC) { 153 if (rule->action == FR_ACT_TO_TBL) { 154 struct dn_fib_table *table; 155 156 table = dn_fib_empty_table(); 157 if (table == NULL) { 158 err = -ENOBUFS; 159 goto errout; 160 } 161 162 rule->table = table->n; 163 } 164 } 165 166 if (tb[FRA_SRC]) 167 r->src = nla_get_u16(tb[FRA_SRC]); 168 169 if (tb[FRA_DST]) 170 r->dst = nla_get_u16(tb[FRA_DST]); 171 172#ifdef CONFIG_DECNET_ROUTE_FWMARK 173 if (tb[FRA_FWMARK]) { 174 r->fwmark = nla_get_u32(tb[FRA_FWMARK]); 175 if (r->fwmark) 176 /* compatibility: if the mark value is non-zero all bits 177 * are compared unless a mask is explicitly specified. 178 */ 179 r->fwmask = 0xFFFFFFFF; 180 } 181 182 if (tb[FRA_FWMASK]) 183 r->fwmask = nla_get_u32(tb[FRA_FWMASK]); 184#endif 185 186 r->src_len = frh->src_len; 187 r->srcmask = dnet_make_mask(r->src_len); 188 r->dst_len = frh->dst_len; 189 r->dstmask = dnet_make_mask(r->dst_len); 190 err = 0; 191errout: 192 return err; 193} 194 195static int dn_fib_rule_compare(struct fib_rule *rule, struct fib_rule_hdr *frh, 196 struct nlattr **tb) 197{ 198 struct dn_fib_rule *r = (struct dn_fib_rule *)rule; 199 200 if (frh->src_len && (r->src_len != frh->src_len)) 201 return 0; 202 203 if (frh->dst_len && (r->dst_len != frh->dst_len)) 204 return 0; 205 206#ifdef CONFIG_DECNET_ROUTE_FWMARK 207 if (tb[FRA_FWMARK] && (r->fwmark != nla_get_u32(tb[FRA_FWMARK]))) 208 return 0; 209 210 if (tb[FRA_FWMASK] && (r->fwmask != nla_get_u32(tb[FRA_FWMASK]))) 211 return 0; 212#endif 213 214 if (tb[FRA_SRC] && (r->src != nla_get_u16(tb[FRA_SRC]))) 215 return 0; 216 217 if (tb[FRA_DST] && (r->dst != nla_get_u16(tb[FRA_DST]))) 218 return 0; 219 220 return 1; 221} 222 223unsigned dnet_addr_type(__le16 addr) 224{ 225 struct flowi fl = { .nl_u = { .dn_u = { .daddr = addr } } }; 226 struct dn_fib_res res; 227 unsigned ret = RTN_UNICAST; 228 struct dn_fib_table *tb = dn_fib_get_table(RT_TABLE_LOCAL, 0); 229 230 res.r = NULL; 231 232 if (tb) { 233 if (!tb->lookup(tb, &fl, &res)) { 234 ret = res.type; 235 dn_fib_res_put(&res); 236 } 237 } 238 return ret; 239} 240 241static int dn_fib_rule_fill(struct fib_rule *rule, struct sk_buff *skb, 242 struct nlmsghdr *nlh, struct fib_rule_hdr *frh) 243{ 244 struct dn_fib_rule *r = (struct dn_fib_rule *)rule; 245 246 frh->family = AF_DECnet; 247 frh->dst_len = r->dst_len; 248 frh->src_len = r->src_len; 249 frh->tos = 0; 250 251#ifdef CONFIG_DECNET_ROUTE_FWMARK 252 if (r->fwmark) 253 NLA_PUT_U32(skb, FRA_FWMARK, r->fwmark); 254 if (r->fwmask || r->fwmark) 255 NLA_PUT_U32(skb, FRA_FWMASK, r->fwmask); 256#endif 257 if (r->dst_len) 258 NLA_PUT_U16(skb, FRA_DST, r->dst); 259 if (r->src_len) 260 NLA_PUT_U16(skb, FRA_SRC, r->src); 261 262 return 0; 263 264nla_put_failure: 265 return -ENOBUFS; 266} 267 268static u32 dn_fib_rule_default_pref(void) 269{ 270 struct list_head *pos; 271 struct fib_rule *rule; 272 273 if (!list_empty(&dn_fib_rules)) { 274 pos = dn_fib_rules.next; 275 if (pos->next != &dn_fib_rules) { 276 rule = list_entry(pos->next, struct fib_rule, list); 277 if (rule->pref) 278 return rule->pref - 1; 279 } 280 } 281 282 return 0; 283} 284 285int dn_fib_dump_rules(struct sk_buff *skb, struct netlink_callback *cb) 286{ 287 return fib_rules_dump(skb, cb, AF_DECnet); 288} 289 290static struct fib_rules_ops dn_fib_rules_ops = { 291 .family = AF_DECnet, 292 .rule_size = sizeof(struct dn_fib_rule), 293 .action = dn_fib_rule_action, 294 .match = dn_fib_rule_match, 295 .configure = dn_fib_rule_configure, 296 .compare = dn_fib_rule_compare, 297 .fill = dn_fib_rule_fill, 298 .default_pref = dn_fib_rule_default_pref, 299 .nlgroup = RTNLGRP_DECnet_RULE, 300 .policy = dn_fib_rule_policy, 301 .rules_list = &dn_fib_rules, 302 .owner = THIS_MODULE, 303}; 304 305void __init dn_fib_rules_init(void) 306{ 307 list_add_tail(&default_rule.common.list, &dn_fib_rules); 308 fib_rules_register(&dn_fib_rules_ops); 309} 310 311void __exit dn_fib_rules_cleanup(void) 312{ 313 fib_rules_unregister(&dn_fib_rules_ops); 314} 315 316