fib_rules.c 7.6 KB
Newer Older
L
Linus Torvalds 已提交
1 2 3 4 5 6 7 8
/*
 * INET		An implementation of the TCP/IP protocol suite for the LINUX
 *		operating system.  INET is implemented using the  BSD Socket
 *		interface as the means of communication with the user level.
 *
 *		IPv4 Forwarding Information Base: policy rules.
 *
 * Authors:	Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
9
 * 		Thomas Graf <tgraf@suug.ch>
L
Linus Torvalds 已提交
10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
 *
 *		This program is free software; you can redistribute it and/or
 *		modify it under the terms of the GNU General Public License
 *		as published by the Free Software Foundation; either version
 *		2 of the License, or (at your option) any later version.
 *
 * Fixes:
 * 		Rani Assaf	:	local_rule cannot be deleted
 *		Marc Boucher	:	routing by fwmark
 */

#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/netdevice.h>
#include <linux/netlink.h>
25
#include <linux/inetdevice.h>
L
Linus Torvalds 已提交
26
#include <linux/init.h>
27 28
#include <linux/list.h>
#include <linux/rcupdate.h>
L
Linus Torvalds 已提交
29 30 31 32
#include <net/ip.h>
#include <net/route.h>
#include <net/tcp.h>
#include <net/ip_fib.h>
33
#include <net/fib_rules.h>
L
Linus Torvalds 已提交
34

35
static struct fib_rules_ops fib4_rules_ops;
L
Linus Torvalds 已提交
36

37
struct fib4_rule
L
Linus Torvalds 已提交
38
{
39 40 41 42 43 44 45 46
	struct fib_rule		common;
	u8			dst_len;
	u8			src_len;
	u8			tos;
	u32			src;
	u32			srcmask;
	u32			dst;
	u32			dstmask;
L
Linus Torvalds 已提交
47
#ifdef CONFIG_IP_ROUTE_FWMARK
48
	u32			fwmark;
L
Linus Torvalds 已提交
49 50
#endif
#ifdef CONFIG_NET_CLS_ROUTE
51
	u32			tclassid;
L
Linus Torvalds 已提交
52 53 54
#endif
};

55 56 57 58 59 60 61
static struct fib4_rule default_rule = {
	.common = {
		.refcnt =	ATOMIC_INIT(2),
		.pref =		0x7FFF,
		.table =	RT_TABLE_DEFAULT,
		.action =	FR_ACT_TO_TBL,
	},
L
Linus Torvalds 已提交
62 63
};

64 65 66 67 68 69 70
static struct fib4_rule main_rule = {
	.common = {
		.refcnt =	ATOMIC_INIT(2),
		.pref =		0x7FFE,
		.table =	RT_TABLE_MAIN,
		.action =	FR_ACT_TO_TBL,
	},
L
Linus Torvalds 已提交
71 72
};

73 74 75 76 77 78 79
static struct fib4_rule local_rule = {
	.common = {
		.refcnt =	ATOMIC_INIT(2),
		.table =	RT_TABLE_LOCAL,
		.action =	FR_ACT_TO_TBL,
		.flags =	FIB_RULE_PERMANENT,
	},
L
Linus Torvalds 已提交
80 81
};

82 83 84 85 86 87 88 89
static LIST_HEAD(fib4_rules);

#ifdef CONFIG_NET_CLS_ROUTE
u32 fib_rules_tclass(struct fib_result *res)
{
	return res->r ? ((struct fib4_rule *) res->r)->tclassid : 0;
}
#endif
90

91 92 93 94 95 96
int fib_lookup(struct flowi *flp, struct fib_result *res)
{
	struct fib_lookup_arg arg = {
		.result = res,
	};
	int err;
L
Linus Torvalds 已提交
97

98 99
	err = fib_rules_lookup(&fib4_rules_ops, flp, 0, &arg);
	res->r = arg.rule;
100

101 102 103 104 105
	return err;
}

int fib4_rule_action(struct fib_rule *rule, struct flowi *flp, int flags,
		     struct fib_lookup_arg *arg)
L
Linus Torvalds 已提交
106
{
107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125
	int err = -EAGAIN;
	struct fib_table *tbl;

	switch (rule->action) {
	case FR_ACT_TO_TBL:
		break;

	case FR_ACT_UNREACHABLE:
		err = -ENETUNREACH;
		goto errout;

	case FR_ACT_PROHIBIT:
		err = -EACCES;
		goto errout;

	case FR_ACT_BLACKHOLE:
	default:
		err = -EINVAL;
		goto errout;
L
Linus Torvalds 已提交
126
	}
127 128 129 130 131 132 133 134

	if ((tbl = fib_get_table(rule->table)) == NULL)
		goto errout;

	err = tbl->tb_lookup(tbl, flp, (struct fib_result *) arg->result);
	if (err > 0)
		err = -EAGAIN;
errout:
L
Linus Torvalds 已提交
135 136 137
	return err;
}

138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168

void fib_select_default(const struct flowi *flp, struct fib_result *res)
{
	if (res->r && res->r->action == FR_ACT_TO_TBL &&
	    FIB_RES_GW(*res) && FIB_RES_NH(*res).nh_scope == RT_SCOPE_LINK) {
		struct fib_table *tb;
		if ((tb = fib_get_table(res->r->table)) != NULL)
			tb->tb_select_default(tb, flp, res);
	}
}

static int fib4_rule_match(struct fib_rule *rule, struct flowi *fl, int flags)
{
	struct fib4_rule *r = (struct fib4_rule *) rule;
	u32 daddr = fl->fl4_dst;
	u32 saddr = fl->fl4_src;

	if (((saddr ^ r->src) & r->srcmask) ||
	    ((daddr ^ r->dst) & r->dstmask))
		return 0;

	if (r->tos && (r->tos != fl->fl4_tos))
		return 0;

#ifdef CONFIG_IP_ROUTE_FWMARK
	if (r->fwmark && (r->fwmark != fl->fl4_fwmark))
		return 0;
#endif

	return 1;
}
L
Linus Torvalds 已提交
169 170 171 172 173 174 175 176 177 178 179

static struct fib_table *fib_empty_table(void)
{
	int id;

	for (id = 1; id <= RT_TABLE_MAX; id++)
		if (fib_tables[id] == NULL)
			return __fib_new_table(id);
	return NULL;
}

180 181 182 183 184 185 186 187
static struct nla_policy fib4_rule_policy[FRA_MAX+1] __read_mostly = {
	[FRA_IFNAME]	= { .type = NLA_STRING },
	[FRA_PRIORITY]	= { .type = NLA_U32 },
	[FRA_SRC]	= { .type = NLA_U32 },
	[FRA_DST]	= { .type = NLA_U32 },
	[FRA_FWMARK]	= { .type = NLA_U32 },
	[FRA_FLOW]	= { .type = NLA_U32 },
};
188

189 190 191
static int fib4_rule_configure(struct fib_rule *rule, struct sk_buff *skb,
			       struct nlmsghdr *nlh, struct fib_rule_hdr *frh,
			       struct nlattr **tb)
L
Linus Torvalds 已提交
192
{
193 194
	int err = -EINVAL;
	struct fib4_rule *rule4 = (struct fib4_rule *) rule;
L
Linus Torvalds 已提交
195

196 197 198
	if (frh->src_len > 32 || frh->dst_len > 32 ||
	    (frh->tos & ~IPTOS_TOS_MASK))
		goto errout;
199

200 201 202
	if (rule->table == RT_TABLE_UNSPEC) {
		if (rule->action == FR_ACT_TO_TBL) {
			struct fib_table *table;
L
Linus Torvalds 已提交
203

204 205 206 207 208
			table = fib_empty_table();
			if (table == NULL) {
				err = -ENOBUFS;
				goto errout;
			}
L
Linus Torvalds 已提交
209

210
			rule->table = table->tb_id;
L
Linus Torvalds 已提交
211 212 213
		}
	}

214 215
	if (tb[FRA_SRC])
		rule4->src = nla_get_u32(tb[FRA_SRC]);
216

217 218
	if (tb[FRA_DST])
		rule4->dst = nla_get_u32(tb[FRA_DST]);
219

220 221 222 223
#ifdef CONFIG_IP_ROUTE_FWMARK
	if (tb[FRA_FWMARK])
		rule4->fwmark = nla_get_u32(tb[FRA_FWMARK]);
#endif
L
Linus Torvalds 已提交
224 225

#ifdef CONFIG_NET_CLS_ROUTE
226 227
	if (tb[FRA_FLOW])
		rule4->tclassid = nla_get_u32(tb[FRA_FLOW]);
L
Linus Torvalds 已提交
228 229
#endif

230 231 232 233 234
	rule4->src_len = frh->src_len;
	rule4->srcmask = inet_make_mask(rule4->src_len);
	rule4->dst_len = frh->dst_len;
	rule4->dstmask = inet_make_mask(rule4->dst_len);
	rule4->tos = frh->tos;
235

236 237 238
	err = 0;
errout:
	return err;
L
Linus Torvalds 已提交
239 240
}

241 242
static int fib4_rule_compare(struct fib_rule *rule, struct fib_rule_hdr *frh,
			     struct nlattr **tb)
L
Linus Torvalds 已提交
243
{
244
	struct fib4_rule *rule4 = (struct fib4_rule *) rule;
L
Linus Torvalds 已提交
245

246 247
	if (frh->src_len && (rule4->src_len != frh->src_len))
		return 0;
L
Linus Torvalds 已提交
248

249 250
	if (frh->dst_len && (rule4->dst_len != frh->dst_len))
		return 0;
251

252 253
	if (frh->tos && (rule4->tos != frh->tos))
		return 0;
254

L
Linus Torvalds 已提交
255
#ifdef CONFIG_IP_ROUTE_FWMARK
256 257
	if (tb[FRA_FWMARK] && (rule4->fwmark != nla_get_u32(tb[FRA_FWMARK])))
		return 0;
L
Linus Torvalds 已提交
258 259
#endif

260 261 262 263
#ifdef CONFIG_NET_CLS_ROUTE
	if (tb[FRA_FLOW] && (rule4->tclassid != nla_get_u32(tb[FRA_FLOW])))
		return 0;
#endif
L
Linus Torvalds 已提交
264

265 266
	if (tb[FRA_SRC] && (rule4->src != nla_get_u32(tb[FRA_SRC])))
		return 0;
L
Linus Torvalds 已提交
267

268 269
	if (tb[FRA_DST] && (rule4->dst != nla_get_u32(tb[FRA_DST])))
		return 0;
L
Linus Torvalds 已提交
270

271
	return 1;
L
Linus Torvalds 已提交
272 273
}

274 275 276 277
static int fib4_rule_fill(struct fib_rule *rule, struct sk_buff *skb,
			  struct nlmsghdr *nlh, struct fib_rule_hdr *frh)
{
	struct fib4_rule *rule4 = (struct fib4_rule *) rule;
L
Linus Torvalds 已提交
278

279 280 281 282
	frh->family = AF_INET;
	frh->dst_len = rule4->dst_len;
	frh->src_len = rule4->src_len;
	frh->tos = rule4->tos;
L
Linus Torvalds 已提交
283 284

#ifdef CONFIG_IP_ROUTE_FWMARK
285 286
	if (rule4->fwmark)
		NLA_PUT_U32(skb, FRA_FWMARK, rule4->fwmark);
L
Linus Torvalds 已提交
287
#endif
288 289 290 291 292 293 294

	if (rule4->dst_len)
		NLA_PUT_U32(skb, FRA_DST, rule4->dst);

	if (rule4->src_len)
		NLA_PUT_U32(skb, FRA_SRC, rule4->src);

L
Linus Torvalds 已提交
295
#ifdef CONFIG_NET_CLS_ROUTE
296 297
	if (rule4->tclassid)
		NLA_PUT_U32(skb, FRA_FLOW, rule4->tclassid);
L
Linus Torvalds 已提交
298
#endif
299
	return 0;
L
Linus Torvalds 已提交
300

301 302
nla_put_failure:
	return -ENOBUFS;
L
Linus Torvalds 已提交
303 304
}

305 306 307 308
int fib4_rules_dump(struct sk_buff *skb, struct netlink_callback *cb)
{
	return fib_rules_dump(skb, cb, AF_INET);
}
309

310
static u32 fib4_rule_default_pref(void)
311
{
312 313 314 315 316 317 318 319 320 321
	struct list_head *pos;
	struct fib_rule *rule;

	if (!list_empty(&fib4_rules)) {
		pos = fib4_rules.next;
		if (pos->next != &fib4_rules) {
			rule = list_entry(pos->next, struct fib_rule, list);
			if (rule->pref)
				return rule->pref - 1;
		}
322
	}
323 324

	return 0;
325 326
}

327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342
static struct fib_rules_ops fib4_rules_ops = {
	.family		= AF_INET,
	.rule_size	= sizeof(struct fib4_rule),
	.action		= fib4_rule_action,
	.match		= fib4_rule_match,
	.configure	= fib4_rule_configure,
	.compare	= fib4_rule_compare,
	.fill		= fib4_rule_fill,
	.default_pref	= fib4_rule_default_pref,
	.nlgroup	= RTNLGRP_IPV4_RULE,
	.policy		= fib4_rule_policy,
	.rules_list	= &fib4_rules,
	.owner		= THIS_MODULE,
};

void __init fib4_rules_init(void)
L
Linus Torvalds 已提交
343
{
344 345 346
	list_add_tail(&local_rule.common.list, &fib4_rules);
	list_add_tail(&main_rule.common.list, &fib4_rules);
	list_add_tail(&default_rule.common.list, &fib4_rules);
L
Linus Torvalds 已提交
347

348
	fib_rules_register(&fib4_rules_ops);
L
Linus Torvalds 已提交
349 350
}

351
void __exit fib4_rules_cleanup(void)
L
Linus Torvalds 已提交
352
{
353
	fib_rules_unregister(&fib4_rules_ops);
L
Linus Torvalds 已提交
354
}