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
	return err;
}

A
Adrian Bunk 已提交
104 105
static 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

static struct fib_table *fib_empty_table(void)
{
172
	u32 id;
L
Linus Torvalds 已提交
173 174 175 176 177 178 179

	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
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 },
187
	[FRA_TABLE]	= { .type = NLA_U32 },
188
};
189

190 191 192
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 已提交
193
{
194 195
	int err = -EINVAL;
	struct fib4_rule *rule4 = (struct fib4_rule *) rule;
L
Linus Torvalds 已提交
196

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

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

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

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

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

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

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

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

231 232 233 234 235
	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;
236

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

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

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

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

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

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

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

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

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

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

275 276 277 278
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 已提交
279

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

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

	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 已提交
296
#ifdef CONFIG_NET_CLS_ROUTE
297 298
	if (rule4->tclassid)
		NLA_PUT_U32(skb, FRA_FLOW, rule4->tclassid);
L
Linus Torvalds 已提交
299
#endif
300
	return 0;
L
Linus Torvalds 已提交
301

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

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

311
static u32 fib4_rule_default_pref(void)
312
{
313 314 315 316 317 318 319 320 321 322
	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;
		}
323
	}
324 325

	return 0;
326 327
}

328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343
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 已提交
344
{
345 346 347
	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 已提交
348

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