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 <linux/export.h>
27#include <net/neighbour.h>
28#include <net/dst.h>
29#include <net/flow.h>
30#include <net/fib_rules.h>
31#include <net/dn.h>
32#include <net/dn_fib.h>
33#include <net/dn_neigh.h>
34#include <net/dn_dev.h>
35#include <net/dn_route.h>
36
37static struct fib_rules_ops *dn_fib_rules_ops;
38
39struct dn_fib_rule
40{
41	struct fib_rule		common;
42	unsigned char		dst_len;
43	unsigned char		src_len;
44	__le16			src;
45	__le16			srcmask;
46	__le16			dst;
47	__le16			dstmask;
48	__le16			srcmap;
49	u8			flags;
50};
51
52
53int dn_fib_lookup(struct flowidn *flp, struct dn_fib_res *res)
54{
55	struct fib_lookup_arg arg = {
56		.result = res,
57	};
58	int err;
59
60	err = fib_rules_lookup(dn_fib_rules_ops,
61			       flowidn_to_flowi(flp), 0, &arg);
62	res->r = arg.rule;
63
64	return err;
65}
66
67static int dn_fib_rule_action(struct fib_rule *rule, struct flowi *flp,
68			      int flags, struct fib_lookup_arg *arg)
69{
70	struct flowidn *fld = &flp->u.dn;
71	int err = -EAGAIN;
72	struct dn_fib_table *tbl;
73
74	switch(rule->action) {
75	case FR_ACT_TO_TBL:
76		break;
77
78	case FR_ACT_UNREACHABLE:
79		err = -ENETUNREACH;
80		goto errout;
81
82	case FR_ACT_PROHIBIT:
83		err = -EACCES;
84		goto errout;
85
86	case FR_ACT_BLACKHOLE:
87	default:
88		err = -EINVAL;
89		goto errout;
90	}
91
92	tbl = dn_fib_get_table(rule->table, 0);
93	if (tbl == NULL)
94		goto errout;
95
96	err = tbl->lookup(tbl, fld, (struct dn_fib_res *)arg->result);
97	if (err > 0)
98		err = -EAGAIN;
99errout:
100	return err;
101}
102
103static const struct nla_policy dn_fib_rule_policy[FRA_MAX+1] = {
104	FRA_GENERIC_POLICY,
105};
106
107static int dn_fib_rule_match(struct fib_rule *rule, struct flowi *fl, int flags)
108{
109	struct dn_fib_rule *r = (struct dn_fib_rule *)rule;
110	struct flowidn *fld = &fl->u.dn;
111	__le16 daddr = fld->daddr;
112	__le16 saddr = fld->saddr;
113
114	if (((saddr ^ r->src) & r->srcmask) ||
115	    ((daddr ^ r->dst) & r->dstmask))
116		return 0;
117
118	return 1;
119}
120
121static int dn_fib_rule_configure(struct fib_rule *rule, struct sk_buff *skb,
122				 struct fib_rule_hdr *frh,
123				 struct nlattr **tb)
124{
125	int err = -EINVAL;
126	struct dn_fib_rule *r = (struct dn_fib_rule *)rule;
127
128	if (frh->tos)
129		goto  errout;
130
131	if (rule->table == RT_TABLE_UNSPEC) {
132		if (rule->action == FR_ACT_TO_TBL) {
133			struct dn_fib_table *table;
134
135			table = dn_fib_empty_table();
136			if (table == NULL) {
137				err = -ENOBUFS;
138				goto errout;
139			}
140
141			rule->table = table->n;
142		}
143	}
144
145	if (frh->src_len)
146		r->src = nla_get_le16(tb[FRA_SRC]);
147
148	if (frh->dst_len)
149		r->dst = nla_get_le16(tb[FRA_DST]);
150
151	r->src_len = frh->src_len;
152	r->srcmask = dnet_make_mask(r->src_len);
153	r->dst_len = frh->dst_len;
154	r->dstmask = dnet_make_mask(r->dst_len);
155	err = 0;
156errout:
157	return err;
158}
159
160static int dn_fib_rule_compare(struct fib_rule *rule, struct fib_rule_hdr *frh,
161			       struct nlattr **tb)
162{
163	struct dn_fib_rule *r = (struct dn_fib_rule *)rule;
164
165	if (frh->src_len && (r->src_len != frh->src_len))
166		return 0;
167
168	if (frh->dst_len && (r->dst_len != frh->dst_len))
169		return 0;
170
171	if (frh->src_len && (r->src != nla_get_le16(tb[FRA_SRC])))
172		return 0;
173
174	if (frh->dst_len && (r->dst != nla_get_le16(tb[FRA_DST])))
175		return 0;
176
177	return 1;
178}
179
180unsigned int dnet_addr_type(__le16 addr)
181{
182	struct flowidn fld = { .daddr = addr };
183	struct dn_fib_res res;
184	unsigned int ret = RTN_UNICAST;
185	struct dn_fib_table *tb = dn_fib_get_table(RT_TABLE_LOCAL, 0);
186
187	res.r = NULL;
188
189	if (tb) {
190		if (!tb->lookup(tb, &fld, &res)) {
191			ret = res.type;
192			dn_fib_res_put(&res);
193		}
194	}
195	return ret;
196}
197
198static int dn_fib_rule_fill(struct fib_rule *rule, struct sk_buff *skb,
199			    struct fib_rule_hdr *frh)
200{
201	struct dn_fib_rule *r = (struct dn_fib_rule *)rule;
202
203	frh->dst_len = r->dst_len;
204	frh->src_len = r->src_len;
205	frh->tos = 0;
206
207	if ((r->dst_len &&
208	     nla_put_le16(skb, FRA_DST, r->dst)) ||
209	    (r->src_len &&
210	     nla_put_le16(skb, FRA_SRC, r->src)))
211		goto nla_put_failure;
212	return 0;
213
214nla_put_failure:
215	return -ENOBUFS;
216}
217
218static void dn_fib_rule_flush_cache(struct fib_rules_ops *ops)
219{
220	dn_rt_cache_flush(-1);
221}
222
223static const struct fib_rules_ops __net_initconst dn_fib_rules_ops_template = {
224	.family		= AF_DECnet,
225	.rule_size	= sizeof(struct dn_fib_rule),
226	.addr_size	= sizeof(u16),
227	.action		= dn_fib_rule_action,
228	.match		= dn_fib_rule_match,
229	.configure	= dn_fib_rule_configure,
230	.compare	= dn_fib_rule_compare,
231	.fill		= dn_fib_rule_fill,
232	.default_pref	= fib_default_rule_pref,
233	.flush_cache	= dn_fib_rule_flush_cache,
234	.nlgroup	= RTNLGRP_DECnet_RULE,
235	.policy		= dn_fib_rule_policy,
236	.owner		= THIS_MODULE,
237	.fro_net	= &init_net,
238};
239
240void __init dn_fib_rules_init(void)
241{
242	dn_fib_rules_ops =
243		fib_rules_register(&dn_fib_rules_ops_template, &init_net);
244	BUG_ON(IS_ERR(dn_fib_rules_ops));
245	BUG_ON(fib_default_rule_add(dn_fib_rules_ops, 0x7fff,
246			            RT_TABLE_MAIN, 0));
247}
248
249void __exit dn_fib_rules_cleanup(void)
250{
251	fib_rules_unregister(dn_fib_rules_ops);
252	rcu_barrier();
253}
254
255
256