diff options
author | Thomas Graf <tgraf@suug.ch> | 2006-08-04 06:39:22 -0400 |
---|---|---|
committer | David S. Miller <davem@sunset.davemloft.net> | 2006-09-22 17:53:42 -0400 |
commit | e1ef4bf23b1ced0bf78a1c98289f746486e5c912 (patch) | |
tree | 9085559a5bf39e2dffa33d2ae4548d7b15d27064 /net/ipv4 | |
parent | 101367c2f8c464ea96643192673aa18d88e6336d (diff) |
[IPV4]: Use Protocol Independant Policy Routing Rules Framework
Signed-off-by: Thomas Graf <tgraf@suug.ch>
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net/ipv4')
-rw-r--r-- | net/ipv4/Kconfig | 1 | ||||
-rw-r--r-- | net/ipv4/devinet.c | 4 | ||||
-rw-r--r-- | net/ipv4/fib_frontend.c | 2 | ||||
-rw-r--r-- | net/ipv4/fib_rules.c | 605 |
4 files changed, 242 insertions, 370 deletions
diff --git a/net/ipv4/Kconfig b/net/ipv4/Kconfig index 3b5d504a74be..1650b64415aa 100644 --- a/net/ipv4/Kconfig +++ b/net/ipv4/Kconfig | |||
@@ -88,6 +88,7 @@ config IP_FIB_HASH | |||
88 | config IP_MULTIPLE_TABLES | 88 | config IP_MULTIPLE_TABLES |
89 | bool "IP: policy routing" | 89 | bool "IP: policy routing" |
90 | depends on IP_ADVANCED_ROUTER | 90 | depends on IP_ADVANCED_ROUTER |
91 | select FIB_RULES | ||
91 | ---help--- | 92 | ---help--- |
92 | Normally, a router decides what to do with a received packet based | 93 | Normally, a router decides what to do with a received packet based |
93 | solely on the packet's final destination address. If you say Y here, | 94 | solely on the packet's final destination address. If you say Y here, |
diff --git a/net/ipv4/devinet.c b/net/ipv4/devinet.c index a6cc31d911eb..9f3ffbec3296 100644 --- a/net/ipv4/devinet.c +++ b/net/ipv4/devinet.c | |||
@@ -1151,9 +1151,7 @@ static struct rtnetlink_link inet_rtnetlink_table[RTM_NR_MSGTYPES] = { | |||
1151 | [RTM_GETROUTE - RTM_BASE] = { .doit = inet_rtm_getroute, | 1151 | [RTM_GETROUTE - RTM_BASE] = { .doit = inet_rtm_getroute, |
1152 | .dumpit = inet_dump_fib, }, | 1152 | .dumpit = inet_dump_fib, }, |
1153 | #ifdef CONFIG_IP_MULTIPLE_TABLES | 1153 | #ifdef CONFIG_IP_MULTIPLE_TABLES |
1154 | [RTM_NEWRULE - RTM_BASE] = { .doit = inet_rtm_newrule, }, | 1154 | [RTM_GETRULE - RTM_BASE] = { .dumpit = fib4_rules_dump, }, |
1155 | [RTM_DELRULE - RTM_BASE] = { .doit = inet_rtm_delrule, }, | ||
1156 | [RTM_GETRULE - RTM_BASE] = { .dumpit = inet_dump_rules, }, | ||
1157 | #endif | 1155 | #endif |
1158 | }; | 1156 | }; |
1159 | 1157 | ||
diff --git a/net/ipv4/fib_frontend.c b/net/ipv4/fib_frontend.c index ba2a70745a63..fe4a53d4d10d 100644 --- a/net/ipv4/fib_frontend.c +++ b/net/ipv4/fib_frontend.c | |||
@@ -656,7 +656,7 @@ void __init ip_fib_init(void) | |||
656 | ip_fib_local_table = fib_hash_init(RT_TABLE_LOCAL); | 656 | ip_fib_local_table = fib_hash_init(RT_TABLE_LOCAL); |
657 | ip_fib_main_table = fib_hash_init(RT_TABLE_MAIN); | 657 | ip_fib_main_table = fib_hash_init(RT_TABLE_MAIN); |
658 | #else | 658 | #else |
659 | fib_rules_init(); | 659 | fib4_rules_init(); |
660 | #endif | 660 | #endif |
661 | 661 | ||
662 | register_netdevice_notifier(&fib_netdev_notifier); | 662 | register_netdevice_notifier(&fib_netdev_notifier); |
diff --git a/net/ipv4/fib_rules.c b/net/ipv4/fib_rules.c index 79b04718bdfd..23ec6ae1a0f6 100644 --- a/net/ipv4/fib_rules.c +++ b/net/ipv4/fib_rules.c | |||
@@ -5,9 +5,8 @@ | |||
5 | * | 5 | * |
6 | * IPv4 Forwarding Information Base: policy rules. | 6 | * IPv4 Forwarding Information Base: policy rules. |
7 | * | 7 | * |
8 | * Version: $Id: fib_rules.c,v 1.17 2001/10/31 21:55:54 davem Exp $ | ||
9 | * | ||
10 | * Authors: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru> | 8 | * Authors: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru> |
9 | * Thomas Graf <tgraf@suug.ch> | ||
11 | * | 10 | * |
12 | * This program is free software; you can redistribute it and/or | 11 | * This program is free software; you can redistribute it and/or |
13 | * modify it under the terms of the GNU General Public License | 12 | * modify it under the terms of the GNU General Public License |
@@ -19,129 +18,154 @@ | |||
19 | * Marc Boucher : routing by fwmark | 18 | * Marc Boucher : routing by fwmark |
20 | */ | 19 | */ |
21 | 20 | ||
22 | #include <asm/uaccess.h> | ||
23 | #include <asm/system.h> | ||
24 | #include <linux/bitops.h> | ||
25 | #include <linux/types.h> | 21 | #include <linux/types.h> |
26 | #include <linux/kernel.h> | 22 | #include <linux/kernel.h> |
27 | #include <linux/sched.h> | ||
28 | #include <linux/mm.h> | ||
29 | #include <linux/string.h> | ||
30 | #include <linux/socket.h> | ||
31 | #include <linux/sockios.h> | ||
32 | #include <linux/errno.h> | ||
33 | #include <linux/in.h> | ||
34 | #include <linux/inet.h> | ||
35 | #include <linux/inetdevice.h> | ||
36 | #include <linux/netdevice.h> | 23 | #include <linux/netdevice.h> |
37 | #include <linux/if_arp.h> | ||
38 | #include <linux/proc_fs.h> | ||
39 | #include <linux/skbuff.h> | ||
40 | #include <linux/netlink.h> | 24 | #include <linux/netlink.h> |
25 | #include <linux/inetdevice.h> | ||
41 | #include <linux/init.h> | 26 | #include <linux/init.h> |
42 | #include <linux/list.h> | 27 | #include <linux/list.h> |
43 | #include <linux/rcupdate.h> | 28 | #include <linux/rcupdate.h> |
44 | |||
45 | #include <net/ip.h> | 29 | #include <net/ip.h> |
46 | #include <net/protocol.h> | ||
47 | #include <net/route.h> | 30 | #include <net/route.h> |
48 | #include <net/tcp.h> | 31 | #include <net/tcp.h> |
49 | #include <net/sock.h> | ||
50 | #include <net/ip_fib.h> | 32 | #include <net/ip_fib.h> |
33 | #include <net/fib_rules.h> | ||
51 | 34 | ||
52 | #define FRprintk(a...) | 35 | static struct fib_rules_ops fib4_rules_ops; |
53 | 36 | ||
54 | struct fib_rule | 37 | struct fib4_rule |
55 | { | 38 | { |
56 | struct hlist_node hlist; | 39 | struct fib_rule common; |
57 | atomic_t r_clntref; | 40 | u8 dst_len; |
58 | u32 r_preference; | 41 | u8 src_len; |
59 | unsigned char r_table; | 42 | u8 tos; |
60 | unsigned char r_action; | 43 | u32 src; |
61 | unsigned char r_dst_len; | 44 | u32 srcmask; |
62 | unsigned char r_src_len; | 45 | u32 dst; |
63 | u32 r_src; | 46 | u32 dstmask; |
64 | u32 r_srcmask; | ||
65 | u32 r_dst; | ||
66 | u32 r_dstmask; | ||
67 | u32 r_srcmap; | ||
68 | u8 r_flags; | ||
69 | u8 r_tos; | ||
70 | #ifdef CONFIG_IP_ROUTE_FWMARK | 47 | #ifdef CONFIG_IP_ROUTE_FWMARK |
71 | u32 r_fwmark; | 48 | u32 fwmark; |
72 | #endif | 49 | #endif |
73 | int r_ifindex; | ||
74 | #ifdef CONFIG_NET_CLS_ROUTE | 50 | #ifdef CONFIG_NET_CLS_ROUTE |
75 | __u32 r_tclassid; | 51 | u32 tclassid; |
76 | #endif | 52 | #endif |
77 | char r_ifname[IFNAMSIZ]; | ||
78 | int r_dead; | ||
79 | struct rcu_head rcu; | ||
80 | }; | 53 | }; |
81 | 54 | ||
82 | static struct fib_rule default_rule = { | 55 | static struct fib4_rule default_rule = { |
83 | .r_clntref = ATOMIC_INIT(2), | 56 | .common = { |
84 | .r_preference = 0x7FFF, | 57 | .refcnt = ATOMIC_INIT(2), |
85 | .r_table = RT_TABLE_DEFAULT, | 58 | .pref = 0x7FFF, |
86 | .r_action = RTN_UNICAST, | 59 | .table = RT_TABLE_DEFAULT, |
60 | .action = FR_ACT_TO_TBL, | ||
61 | }, | ||
87 | }; | 62 | }; |
88 | 63 | ||
89 | static struct fib_rule main_rule = { | 64 | static struct fib4_rule main_rule = { |
90 | .r_clntref = ATOMIC_INIT(2), | 65 | .common = { |
91 | .r_preference = 0x7FFE, | 66 | .refcnt = ATOMIC_INIT(2), |
92 | .r_table = RT_TABLE_MAIN, | 67 | .pref = 0x7FFE, |
93 | .r_action = RTN_UNICAST, | 68 | .table = RT_TABLE_MAIN, |
69 | .action = FR_ACT_TO_TBL, | ||
70 | }, | ||
94 | }; | 71 | }; |
95 | 72 | ||
96 | static struct fib_rule local_rule = { | 73 | static struct fib4_rule local_rule = { |
97 | .r_clntref = ATOMIC_INIT(2), | 74 | .common = { |
98 | .r_table = RT_TABLE_LOCAL, | 75 | .refcnt = ATOMIC_INIT(2), |
99 | .r_action = RTN_UNICAST, | 76 | .table = RT_TABLE_LOCAL, |
77 | .action = FR_ACT_TO_TBL, | ||
78 | .flags = FIB_RULE_PERMANENT, | ||
79 | }, | ||
100 | }; | 80 | }; |
101 | 81 | ||
102 | static struct hlist_head fib_rules; | 82 | static LIST_HEAD(fib4_rules); |
83 | |||
84 | #ifdef CONFIG_NET_CLS_ROUTE | ||
85 | u32 fib_rules_tclass(struct fib_result *res) | ||
86 | { | ||
87 | return res->r ? ((struct fib4_rule *) res->r)->tclassid : 0; | ||
88 | } | ||
89 | #endif | ||
103 | 90 | ||
104 | /* writer func called from netlink -- rtnl_sem hold*/ | 91 | int fib_lookup(struct flowi *flp, struct fib_result *res) |
92 | { | ||
93 | struct fib_lookup_arg arg = { | ||
94 | .result = res, | ||
95 | }; | ||
96 | int err; | ||
105 | 97 | ||
106 | static void rtmsg_rule(int, struct fib_rule *); | 98 | err = fib_rules_lookup(&fib4_rules_ops, flp, 0, &arg); |
99 | res->r = arg.rule; | ||
107 | 100 | ||
108 | int inet_rtm_delrule(struct sk_buff *skb, struct nlmsghdr* nlh, void *arg) | 101 | return err; |
102 | } | ||
103 | |||
104 | int fib4_rule_action(struct fib_rule *rule, struct flowi *flp, int flags, | ||
105 | struct fib_lookup_arg *arg) | ||
109 | { | 106 | { |
110 | struct rtattr **rta = arg; | 107 | int err = -EAGAIN; |
111 | struct rtmsg *rtm = NLMSG_DATA(nlh); | 108 | struct fib_table *tbl; |
112 | struct fib_rule *r; | 109 | |
113 | struct hlist_node *node; | 110 | switch (rule->action) { |
114 | int err = -ESRCH; | 111 | case FR_ACT_TO_TBL: |
115 | 112 | break; | |
116 | hlist_for_each_entry(r, node, &fib_rules, hlist) { | 113 | |
117 | if ((!rta[RTA_SRC-1] || memcmp(RTA_DATA(rta[RTA_SRC-1]), &r->r_src, 4) == 0) && | 114 | case FR_ACT_UNREACHABLE: |
118 | rtm->rtm_src_len == r->r_src_len && | 115 | err = -ENETUNREACH; |
119 | rtm->rtm_dst_len == r->r_dst_len && | 116 | goto errout; |
120 | (!rta[RTA_DST-1] || memcmp(RTA_DATA(rta[RTA_DST-1]), &r->r_dst, 4) == 0) && | 117 | |
121 | rtm->rtm_tos == r->r_tos && | 118 | case FR_ACT_PROHIBIT: |
122 | #ifdef CONFIG_IP_ROUTE_FWMARK | 119 | err = -EACCES; |
123 | (!rta[RTA_PROTOINFO-1] || memcmp(RTA_DATA(rta[RTA_PROTOINFO-1]), &r->r_fwmark, 4) == 0) && | 120 | goto errout; |
124 | #endif | 121 | |
125 | (!rtm->rtm_type || rtm->rtm_type == r->r_action) && | 122 | case FR_ACT_BLACKHOLE: |
126 | (!rta[RTA_PRIORITY-1] || memcmp(RTA_DATA(rta[RTA_PRIORITY-1]), &r->r_preference, 4) == 0) && | 123 | default: |
127 | (!rta[RTA_IIF-1] || rtattr_strcmp(rta[RTA_IIF-1], r->r_ifname) == 0) && | 124 | err = -EINVAL; |
128 | (!rtm->rtm_table || (r && rtm->rtm_table == r->r_table))) { | 125 | goto errout; |
129 | err = -EPERM; | ||
130 | if (r == &local_rule) | ||
131 | break; | ||
132 | |||
133 | hlist_del_rcu(&r->hlist); | ||
134 | r->r_dead = 1; | ||
135 | rtmsg_rule(RTM_DELRULE, r); | ||
136 | fib_rule_put(r); | ||
137 | err = 0; | ||
138 | break; | ||
139 | } | ||
140 | } | 126 | } |
127 | |||
128 | if ((tbl = fib_get_table(rule->table)) == NULL) | ||
129 | goto errout; | ||
130 | |||
131 | err = tbl->tb_lookup(tbl, flp, (struct fib_result *) arg->result); | ||
132 | if (err > 0) | ||
133 | err = -EAGAIN; | ||
134 | errout: | ||
141 | return err; | 135 | return err; |
142 | } | 136 | } |
143 | 137 | ||
144 | /* Allocate new unique table id */ | 138 | |
139 | void fib_select_default(const struct flowi *flp, struct fib_result *res) | ||
140 | { | ||
141 | if (res->r && res->r->action == FR_ACT_TO_TBL && | ||
142 | FIB_RES_GW(*res) && FIB_RES_NH(*res).nh_scope == RT_SCOPE_LINK) { | ||
143 | struct fib_table *tb; | ||
144 | if ((tb = fib_get_table(res->r->table)) != NULL) | ||
145 | tb->tb_select_default(tb, flp, res); | ||
146 | } | ||
147 | } | ||
148 | |||
149 | static int fib4_rule_match(struct fib_rule *rule, struct flowi *fl, int flags) | ||
150 | { | ||
151 | struct fib4_rule *r = (struct fib4_rule *) rule; | ||
152 | u32 daddr = fl->fl4_dst; | ||
153 | u32 saddr = fl->fl4_src; | ||
154 | |||
155 | if (((saddr ^ r->src) & r->srcmask) || | ||
156 | ((daddr ^ r->dst) & r->dstmask)) | ||
157 | return 0; | ||
158 | |||
159 | if (r->tos && (r->tos != fl->fl4_tos)) | ||
160 | return 0; | ||
161 | |||
162 | #ifdef CONFIG_IP_ROUTE_FWMARK | ||
163 | if (r->fwmark && (r->fwmark != fl->fl4_fwmark)) | ||
164 | return 0; | ||
165 | #endif | ||
166 | |||
167 | return 1; | ||
168 | } | ||
145 | 169 | ||
146 | static struct fib_table *fib_empty_table(void) | 170 | static struct fib_table *fib_empty_table(void) |
147 | { | 171 | { |
@@ -153,329 +177,178 @@ static struct fib_table *fib_empty_table(void) | |||
153 | return NULL; | 177 | return NULL; |
154 | } | 178 | } |
155 | 179 | ||
156 | static inline void fib_rule_put_rcu(struct rcu_head *head) | 180 | static struct nla_policy fib4_rule_policy[FRA_MAX+1] __read_mostly = { |
157 | { | 181 | [FRA_IFNAME] = { .type = NLA_STRING }, |
158 | struct fib_rule *r = container_of(head, struct fib_rule, rcu); | 182 | [FRA_PRIORITY] = { .type = NLA_U32 }, |
159 | kfree(r); | 183 | [FRA_SRC] = { .type = NLA_U32 }, |
160 | } | 184 | [FRA_DST] = { .type = NLA_U32 }, |
185 | [FRA_FWMARK] = { .type = NLA_U32 }, | ||
186 | [FRA_FLOW] = { .type = NLA_U32 }, | ||
187 | }; | ||
161 | 188 | ||
162 | void fib_rule_put(struct fib_rule *r) | 189 | static int fib4_rule_configure(struct fib_rule *rule, struct sk_buff *skb, |
190 | struct nlmsghdr *nlh, struct fib_rule_hdr *frh, | ||
191 | struct nlattr **tb) | ||
163 | { | 192 | { |
164 | if (atomic_dec_and_test(&r->r_clntref)) { | 193 | int err = -EINVAL; |
165 | if (r->r_dead) | 194 | struct fib4_rule *rule4 = (struct fib4_rule *) rule; |
166 | call_rcu(&r->rcu, fib_rule_put_rcu); | ||
167 | else | ||
168 | printk("Freeing alive rule %p\n", r); | ||
169 | } | ||
170 | } | ||
171 | 195 | ||
172 | /* writer func called from netlink -- rtnl_sem hold*/ | 196 | if (frh->src_len > 32 || frh->dst_len > 32 || |
197 | (frh->tos & ~IPTOS_TOS_MASK)) | ||
198 | goto errout; | ||
173 | 199 | ||
174 | int inet_rtm_newrule(struct sk_buff *skb, struct nlmsghdr* nlh, void *arg) | 200 | if (rule->table == RT_TABLE_UNSPEC) { |
175 | { | 201 | if (rule->action == FR_ACT_TO_TBL) { |
176 | struct rtattr **rta = arg; | 202 | struct fib_table *table; |
177 | struct rtmsg *rtm = NLMSG_DATA(nlh); | ||
178 | struct fib_rule *r, *new_r, *last = NULL; | ||
179 | struct hlist_node *node = NULL; | ||
180 | unsigned char table_id; | ||
181 | |||
182 | if (rtm->rtm_src_len > 32 || rtm->rtm_dst_len > 32 || | ||
183 | (rtm->rtm_tos & ~IPTOS_TOS_MASK)) | ||
184 | return -EINVAL; | ||
185 | |||
186 | if (rta[RTA_IIF-1] && RTA_PAYLOAD(rta[RTA_IIF-1]) > IFNAMSIZ) | ||
187 | return -EINVAL; | ||
188 | |||
189 | table_id = rtm->rtm_table; | ||
190 | if (table_id == RT_TABLE_UNSPEC) { | ||
191 | struct fib_table *table; | ||
192 | if (rtm->rtm_type == RTN_UNICAST) { | ||
193 | if ((table = fib_empty_table()) == NULL) | ||
194 | return -ENOBUFS; | ||
195 | table_id = table->tb_id; | ||
196 | } | ||
197 | } | ||
198 | 203 | ||
199 | new_r = kzalloc(sizeof(*new_r), GFP_KERNEL); | 204 | table = fib_empty_table(); |
200 | if (!new_r) | 205 | if (table == NULL) { |
201 | return -ENOMEM; | 206 | err = -ENOBUFS; |
202 | 207 | goto errout; | |
203 | if (rta[RTA_SRC-1]) | 208 | } |
204 | memcpy(&new_r->r_src, RTA_DATA(rta[RTA_SRC-1]), 4); | ||
205 | if (rta[RTA_DST-1]) | ||
206 | memcpy(&new_r->r_dst, RTA_DATA(rta[RTA_DST-1]), 4); | ||
207 | if (rta[RTA_GATEWAY-1]) | ||
208 | memcpy(&new_r->r_srcmap, RTA_DATA(rta[RTA_GATEWAY-1]), 4); | ||
209 | new_r->r_src_len = rtm->rtm_src_len; | ||
210 | new_r->r_dst_len = rtm->rtm_dst_len; | ||
211 | new_r->r_srcmask = inet_make_mask(rtm->rtm_src_len); | ||
212 | new_r->r_dstmask = inet_make_mask(rtm->rtm_dst_len); | ||
213 | new_r->r_tos = rtm->rtm_tos; | ||
214 | #ifdef CONFIG_IP_ROUTE_FWMARK | ||
215 | if (rta[RTA_PROTOINFO-1]) | ||
216 | memcpy(&new_r->r_fwmark, RTA_DATA(rta[RTA_PROTOINFO-1]), 4); | ||
217 | #endif | ||
218 | new_r->r_action = rtm->rtm_type; | ||
219 | new_r->r_flags = rtm->rtm_flags; | ||
220 | if (rta[RTA_PRIORITY-1]) | ||
221 | memcpy(&new_r->r_preference, RTA_DATA(rta[RTA_PRIORITY-1]), 4); | ||
222 | new_r->r_table = table_id; | ||
223 | if (rta[RTA_IIF-1]) { | ||
224 | struct net_device *dev; | ||
225 | rtattr_strlcpy(new_r->r_ifname, rta[RTA_IIF-1], IFNAMSIZ); | ||
226 | new_r->r_ifindex = -1; | ||
227 | dev = __dev_get_by_name(new_r->r_ifname); | ||
228 | if (dev) | ||
229 | new_r->r_ifindex = dev->ifindex; | ||
230 | } | ||
231 | #ifdef CONFIG_NET_CLS_ROUTE | ||
232 | if (rta[RTA_FLOW-1]) | ||
233 | memcpy(&new_r->r_tclassid, RTA_DATA(rta[RTA_FLOW-1]), 4); | ||
234 | #endif | ||
235 | r = container_of(fib_rules.first, struct fib_rule, hlist); | ||
236 | 209 | ||
237 | if (!new_r->r_preference) { | 210 | rule->table = table->tb_id; |
238 | if (r && r->hlist.next != NULL) { | ||
239 | r = container_of(r->hlist.next, struct fib_rule, hlist); | ||
240 | if (r->r_preference) | ||
241 | new_r->r_preference = r->r_preference - 1; | ||
242 | } | 211 | } |
243 | } | 212 | } |
244 | 213 | ||
245 | hlist_for_each_entry(r, node, &fib_rules, hlist) { | 214 | if (tb[FRA_SRC]) |
246 | if (r->r_preference > new_r->r_preference) | 215 | rule4->src = nla_get_u32(tb[FRA_SRC]); |
247 | break; | ||
248 | last = r; | ||
249 | } | ||
250 | atomic_inc(&new_r->r_clntref); | ||
251 | 216 | ||
252 | if (last) | 217 | if (tb[FRA_DST]) |
253 | hlist_add_after_rcu(&last->hlist, &new_r->hlist); | 218 | rule4->dst = nla_get_u32(tb[FRA_DST]); |
254 | else | ||
255 | hlist_add_before_rcu(&new_r->hlist, &r->hlist); | ||
256 | 219 | ||
257 | rtmsg_rule(RTM_NEWRULE, new_r); | 220 | #ifdef CONFIG_IP_ROUTE_FWMARK |
258 | return 0; | 221 | if (tb[FRA_FWMARK]) |
259 | } | 222 | rule4->fwmark = nla_get_u32(tb[FRA_FWMARK]); |
223 | #endif | ||
260 | 224 | ||
261 | #ifdef CONFIG_NET_CLS_ROUTE | 225 | #ifdef CONFIG_NET_CLS_ROUTE |
262 | u32 fib_rules_tclass(struct fib_result *res) | 226 | if (tb[FRA_FLOW]) |
263 | { | 227 | rule4->tclassid = nla_get_u32(tb[FRA_FLOW]); |
264 | if (res->r) | ||
265 | return res->r->r_tclassid; | ||
266 | return 0; | ||
267 | } | ||
268 | #endif | 228 | #endif |
269 | 229 | ||
270 | /* callers should hold rtnl semaphore */ | 230 | rule4->src_len = frh->src_len; |
271 | 231 | rule4->srcmask = inet_make_mask(rule4->src_len); | |
272 | static void fib_rules_detach(struct net_device *dev) | 232 | rule4->dst_len = frh->dst_len; |
273 | { | 233 | rule4->dstmask = inet_make_mask(rule4->dst_len); |
274 | struct hlist_node *node; | 234 | rule4->tos = frh->tos; |
275 | struct fib_rule *r; | ||
276 | |||
277 | hlist_for_each_entry(r, node, &fib_rules, hlist) { | ||
278 | if (r->r_ifindex == dev->ifindex) | ||
279 | r->r_ifindex = -1; | ||
280 | 235 | ||
281 | } | 236 | err = 0; |
282 | } | 237 | errout: |
283 | 238 | return err; | |
284 | /* callers should hold rtnl semaphore */ | ||
285 | |||
286 | static void fib_rules_attach(struct net_device *dev) | ||
287 | { | ||
288 | struct hlist_node *node; | ||
289 | struct fib_rule *r; | ||
290 | |||
291 | hlist_for_each_entry(r, node, &fib_rules, hlist) { | ||
292 | if (r->r_ifindex == -1 && strcmp(dev->name, r->r_ifname) == 0) | ||
293 | r->r_ifindex = dev->ifindex; | ||
294 | } | ||
295 | } | 239 | } |
296 | 240 | ||
297 | int fib_lookup(const struct flowi *flp, struct fib_result *res) | 241 | static int fib4_rule_compare(struct fib_rule *rule, struct fib_rule_hdr *frh, |
242 | struct nlattr **tb) | ||
298 | { | 243 | { |
299 | int err; | 244 | struct fib4_rule *rule4 = (struct fib4_rule *) rule; |
300 | struct fib_rule *r, *policy; | ||
301 | struct fib_table *tb; | ||
302 | struct hlist_node *node; | ||
303 | 245 | ||
304 | u32 daddr = flp->fl4_dst; | 246 | if (frh->src_len && (rule4->src_len != frh->src_len)) |
305 | u32 saddr = flp->fl4_src; | 247 | return 0; |
306 | 248 | ||
307 | FRprintk("Lookup: %u.%u.%u.%u <- %u.%u.%u.%u ", | 249 | if (frh->dst_len && (rule4->dst_len != frh->dst_len)) |
308 | NIPQUAD(flp->fl4_dst), NIPQUAD(flp->fl4_src)); | 250 | return 0; |
309 | 251 | ||
310 | rcu_read_lock(); | 252 | if (frh->tos && (rule4->tos != frh->tos)) |
253 | return 0; | ||
311 | 254 | ||
312 | hlist_for_each_entry_rcu(r, node, &fib_rules, hlist) { | ||
313 | if (((saddr^r->r_src) & r->r_srcmask) || | ||
314 | ((daddr^r->r_dst) & r->r_dstmask) || | ||
315 | (r->r_tos && r->r_tos != flp->fl4_tos) || | ||
316 | #ifdef CONFIG_IP_ROUTE_FWMARK | 255 | #ifdef CONFIG_IP_ROUTE_FWMARK |
317 | (r->r_fwmark && r->r_fwmark != flp->fl4_fwmark) || | 256 | if (tb[FRA_FWMARK] && (rule4->fwmark != nla_get_u32(tb[FRA_FWMARK]))) |
257 | return 0; | ||
318 | #endif | 258 | #endif |
319 | (r->r_ifindex && r->r_ifindex != flp->iif)) | ||
320 | continue; | ||
321 | |||
322 | FRprintk("tb %d r %d ", r->r_table, r->r_action); | ||
323 | switch (r->r_action) { | ||
324 | case RTN_UNICAST: | ||
325 | policy = r; | ||
326 | break; | ||
327 | case RTN_UNREACHABLE: | ||
328 | rcu_read_unlock(); | ||
329 | return -ENETUNREACH; | ||
330 | default: | ||
331 | case RTN_BLACKHOLE: | ||
332 | rcu_read_unlock(); | ||
333 | return -EINVAL; | ||
334 | case RTN_PROHIBIT: | ||
335 | rcu_read_unlock(); | ||
336 | return -EACCES; | ||
337 | } | ||
338 | 259 | ||
339 | if ((tb = fib_get_table(r->r_table)) == NULL) | 260 | #ifdef CONFIG_NET_CLS_ROUTE |
340 | continue; | 261 | if (tb[FRA_FLOW] && (rule4->tclassid != nla_get_u32(tb[FRA_FLOW]))) |
341 | err = tb->tb_lookup(tb, flp, res); | 262 | return 0; |
342 | if (err == 0) { | 263 | #endif |
343 | res->r = policy; | ||
344 | if (policy) | ||
345 | atomic_inc(&policy->r_clntref); | ||
346 | rcu_read_unlock(); | ||
347 | return 0; | ||
348 | } | ||
349 | if (err < 0 && err != -EAGAIN) { | ||
350 | rcu_read_unlock(); | ||
351 | return err; | ||
352 | } | ||
353 | } | ||
354 | FRprintk("FAILURE\n"); | ||
355 | rcu_read_unlock(); | ||
356 | return -ENETUNREACH; | ||
357 | } | ||
358 | 264 | ||
359 | void fib_select_default(const struct flowi *flp, struct fib_result *res) | 265 | if (tb[FRA_SRC] && (rule4->src != nla_get_u32(tb[FRA_SRC]))) |
360 | { | 266 | return 0; |
361 | if (res->r && res->r->r_action == RTN_UNICAST && | ||
362 | FIB_RES_GW(*res) && FIB_RES_NH(*res).nh_scope == RT_SCOPE_LINK) { | ||
363 | struct fib_table *tb; | ||
364 | if ((tb = fib_get_table(res->r->r_table)) != NULL) | ||
365 | tb->tb_select_default(tb, flp, res); | ||
366 | } | ||
367 | } | ||
368 | 267 | ||
369 | static int fib_rules_event(struct notifier_block *this, unsigned long event, void *ptr) | 268 | if (tb[FRA_DST] && (rule4->dst != nla_get_u32(tb[FRA_DST]))) |
370 | { | 269 | return 0; |
371 | struct net_device *dev = ptr; | ||
372 | 270 | ||
373 | if (event == NETDEV_UNREGISTER) | 271 | return 1; |
374 | fib_rules_detach(dev); | ||
375 | else if (event == NETDEV_REGISTER) | ||
376 | fib_rules_attach(dev); | ||
377 | return NOTIFY_DONE; | ||
378 | } | 272 | } |
379 | 273 | ||
274 | static int fib4_rule_fill(struct fib_rule *rule, struct sk_buff *skb, | ||
275 | struct nlmsghdr *nlh, struct fib_rule_hdr *frh) | ||
276 | { | ||
277 | struct fib4_rule *rule4 = (struct fib4_rule *) rule; | ||
380 | 278 | ||
381 | static struct notifier_block fib_rules_notifier = { | 279 | frh->family = AF_INET; |
382 | .notifier_call =fib_rules_event, | 280 | frh->dst_len = rule4->dst_len; |
383 | }; | 281 | frh->src_len = rule4->src_len; |
282 | frh->tos = rule4->tos; | ||
384 | 283 | ||
385 | static __inline__ int inet_fill_rule(struct sk_buff *skb, | ||
386 | struct fib_rule *r, | ||
387 | u32 pid, u32 seq, int event, | ||
388 | unsigned int flags) | ||
389 | { | ||
390 | struct rtmsg *rtm; | ||
391 | struct nlmsghdr *nlh; | ||
392 | unsigned char *b = skb->tail; | ||
393 | |||
394 | nlh = NLMSG_NEW(skb, pid, seq, event, sizeof(*rtm), flags); | ||
395 | rtm = NLMSG_DATA(nlh); | ||
396 | rtm->rtm_family = AF_INET; | ||
397 | rtm->rtm_dst_len = r->r_dst_len; | ||
398 | rtm->rtm_src_len = r->r_src_len; | ||
399 | rtm->rtm_tos = r->r_tos; | ||
400 | #ifdef CONFIG_IP_ROUTE_FWMARK | 284 | #ifdef CONFIG_IP_ROUTE_FWMARK |
401 | if (r->r_fwmark) | 285 | if (rule4->fwmark) |
402 | RTA_PUT(skb, RTA_PROTOINFO, 4, &r->r_fwmark); | 286 | NLA_PUT_U32(skb, FRA_FWMARK, rule4->fwmark); |
403 | #endif | 287 | #endif |
404 | rtm->rtm_table = r->r_table; | 288 | |
405 | rtm->rtm_protocol = 0; | 289 | if (rule4->dst_len) |
406 | rtm->rtm_scope = 0; | 290 | NLA_PUT_U32(skb, FRA_DST, rule4->dst); |
407 | rtm->rtm_type = r->r_action; | 291 | |
408 | rtm->rtm_flags = r->r_flags; | 292 | if (rule4->src_len) |
409 | 293 | NLA_PUT_U32(skb, FRA_SRC, rule4->src); | |
410 | if (r->r_dst_len) | 294 | |
411 | RTA_PUT(skb, RTA_DST, 4, &r->r_dst); | ||
412 | if (r->r_src_len) | ||
413 | RTA_PUT(skb, RTA_SRC, 4, &r->r_src); | ||
414 | if (r->r_ifname[0]) | ||
415 | RTA_PUT(skb, RTA_IIF, IFNAMSIZ, &r->r_ifname); | ||
416 | if (r->r_preference) | ||
417 | RTA_PUT(skb, RTA_PRIORITY, 4, &r->r_preference); | ||
418 | if (r->r_srcmap) | ||
419 | RTA_PUT(skb, RTA_GATEWAY, 4, &r->r_srcmap); | ||
420 | #ifdef CONFIG_NET_CLS_ROUTE | 295 | #ifdef CONFIG_NET_CLS_ROUTE |
421 | if (r->r_tclassid) | 296 | if (rule4->tclassid) |
422 | RTA_PUT(skb, RTA_FLOW, 4, &r->r_tclassid); | 297 | NLA_PUT_U32(skb, FRA_FLOW, rule4->tclassid); |
423 | #endif | 298 | #endif |
424 | nlh->nlmsg_len = skb->tail - b; | 299 | return 0; |
425 | return skb->len; | ||
426 | 300 | ||
427 | nlmsg_failure: | 301 | nla_put_failure: |
428 | rtattr_failure: | 302 | return -ENOBUFS; |
429 | skb_trim(skb, b - skb->data); | ||
430 | return -1; | ||
431 | } | 303 | } |
432 | 304 | ||
433 | /* callers should hold rtnl semaphore */ | 305 | int fib4_rules_dump(struct sk_buff *skb, struct netlink_callback *cb) |
306 | { | ||
307 | return fib_rules_dump(skb, cb, AF_INET); | ||
308 | } | ||
434 | 309 | ||
435 | static void rtmsg_rule(int event, struct fib_rule *r) | 310 | static u32 fib4_rule_default_pref(void) |
436 | { | 311 | { |
437 | int size = NLMSG_SPACE(sizeof(struct rtmsg) + 128); | 312 | struct list_head *pos; |
438 | struct sk_buff *skb = alloc_skb(size, GFP_KERNEL); | 313 | struct fib_rule *rule; |
439 | 314 | ||
440 | if (!skb) | 315 | if (!list_empty(&fib4_rules)) { |
441 | netlink_set_err(rtnl, 0, RTNLGRP_IPV4_RULE, ENOBUFS); | 316 | pos = fib4_rules.next; |
442 | else if (inet_fill_rule(skb, r, 0, 0, event, 0) < 0) { | 317 | if (pos->next != &fib4_rules) { |
443 | kfree_skb(skb); | 318 | rule = list_entry(pos->next, struct fib_rule, list); |
444 | netlink_set_err(rtnl, 0, RTNLGRP_IPV4_RULE, EINVAL); | 319 | if (rule->pref) |
445 | } else { | 320 | return rule->pref - 1; |
446 | netlink_broadcast(rtnl, skb, 0, RTNLGRP_IPV4_RULE, GFP_KERNEL); | 321 | } |
447 | } | 322 | } |
323 | |||
324 | return 0; | ||
448 | } | 325 | } |
449 | 326 | ||
450 | int inet_dump_rules(struct sk_buff *skb, struct netlink_callback *cb) | 327 | static struct fib_rules_ops fib4_rules_ops = { |
328 | .family = AF_INET, | ||
329 | .rule_size = sizeof(struct fib4_rule), | ||
330 | .action = fib4_rule_action, | ||
331 | .match = fib4_rule_match, | ||
332 | .configure = fib4_rule_configure, | ||
333 | .compare = fib4_rule_compare, | ||
334 | .fill = fib4_rule_fill, | ||
335 | .default_pref = fib4_rule_default_pref, | ||
336 | .nlgroup = RTNLGRP_IPV4_RULE, | ||
337 | .policy = fib4_rule_policy, | ||
338 | .rules_list = &fib4_rules, | ||
339 | .owner = THIS_MODULE, | ||
340 | }; | ||
341 | |||
342 | void __init fib4_rules_init(void) | ||
451 | { | 343 | { |
452 | int idx = 0; | 344 | list_add_tail(&local_rule.common.list, &fib4_rules); |
453 | int s_idx = cb->args[0]; | 345 | list_add_tail(&main_rule.common.list, &fib4_rules); |
454 | struct fib_rule *r; | 346 | list_add_tail(&default_rule.common.list, &fib4_rules); |
455 | struct hlist_node *node; | ||
456 | |||
457 | rcu_read_lock(); | ||
458 | hlist_for_each_entry(r, node, &fib_rules, hlist) { | ||
459 | if (idx < s_idx) | ||
460 | goto next; | ||
461 | if (inet_fill_rule(skb, r, NETLINK_CB(cb->skb).pid, | ||
462 | cb->nlh->nlmsg_seq, | ||
463 | RTM_NEWRULE, NLM_F_MULTI) < 0) | ||
464 | break; | ||
465 | next: | ||
466 | idx++; | ||
467 | } | ||
468 | rcu_read_unlock(); | ||
469 | cb->args[0] = idx; | ||
470 | 347 | ||
471 | return skb->len; | 348 | fib_rules_register(&fib4_rules_ops); |
472 | } | 349 | } |
473 | 350 | ||
474 | void __init fib_rules_init(void) | 351 | void __exit fib4_rules_cleanup(void) |
475 | { | 352 | { |
476 | INIT_HLIST_HEAD(&fib_rules); | 353 | fib_rules_unregister(&fib4_rules_ops); |
477 | hlist_add_head(&local_rule.hlist, &fib_rules); | ||
478 | hlist_add_after(&local_rule.hlist, &main_rule.hlist); | ||
479 | hlist_add_after(&main_rule.hlist, &default_rule.hlist); | ||
480 | register_netdevice_notifier(&fib_rules_notifier); | ||
481 | } | 354 | } |