11da177e4SLinus Torvalds /* 21da177e4SLinus Torvalds * INET An implementation of the TCP/IP protocol suite for the LINUX 31da177e4SLinus Torvalds * operating system. INET is implemented using the BSD Socket 41da177e4SLinus Torvalds * interface as the means of communication with the user level. 51da177e4SLinus Torvalds * 61da177e4SLinus Torvalds * IPv4 Forwarding Information Base: policy rules. 71da177e4SLinus Torvalds * 81da177e4SLinus Torvalds * Version: $Id: fib_rules.c,v 1.17 2001/10/31 21:55:54 davem Exp $ 91da177e4SLinus Torvalds * 101da177e4SLinus Torvalds * Authors: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru> 111da177e4SLinus Torvalds * 121da177e4SLinus Torvalds * This program is free software; you can redistribute it and/or 131da177e4SLinus Torvalds * modify it under the terms of the GNU General Public License 141da177e4SLinus Torvalds * as published by the Free Software Foundation; either version 151da177e4SLinus Torvalds * 2 of the License, or (at your option) any later version. 161da177e4SLinus Torvalds * 171da177e4SLinus Torvalds * Fixes: 181da177e4SLinus Torvalds * Rani Assaf : local_rule cannot be deleted 191da177e4SLinus Torvalds * Marc Boucher : routing by fwmark 201da177e4SLinus Torvalds */ 211da177e4SLinus Torvalds 221da177e4SLinus Torvalds #include <linux/config.h> 231da177e4SLinus Torvalds #include <asm/uaccess.h> 241da177e4SLinus Torvalds #include <asm/system.h> 251da177e4SLinus Torvalds #include <linux/bitops.h> 261da177e4SLinus Torvalds #include <linux/types.h> 271da177e4SLinus Torvalds #include <linux/kernel.h> 281da177e4SLinus Torvalds #include <linux/sched.h> 291da177e4SLinus Torvalds #include <linux/mm.h> 301da177e4SLinus Torvalds #include <linux/string.h> 311da177e4SLinus Torvalds #include <linux/socket.h> 321da177e4SLinus Torvalds #include <linux/sockios.h> 331da177e4SLinus Torvalds #include <linux/errno.h> 341da177e4SLinus Torvalds #include <linux/in.h> 351da177e4SLinus Torvalds #include <linux/inet.h> 3614c85021SArnaldo Carvalho de Melo #include <linux/inetdevice.h> 371da177e4SLinus Torvalds #include <linux/netdevice.h> 381da177e4SLinus Torvalds #include <linux/if_arp.h> 391da177e4SLinus Torvalds #include <linux/proc_fs.h> 401da177e4SLinus Torvalds #include <linux/skbuff.h> 411da177e4SLinus Torvalds #include <linux/netlink.h> 421da177e4SLinus Torvalds #include <linux/init.h> 437b204afdSRobert Olsson #include <linux/list.h> 447b204afdSRobert Olsson #include <linux/rcupdate.h> 451da177e4SLinus Torvalds 461da177e4SLinus Torvalds #include <net/ip.h> 471da177e4SLinus Torvalds #include <net/protocol.h> 481da177e4SLinus Torvalds #include <net/route.h> 491da177e4SLinus Torvalds #include <net/tcp.h> 501da177e4SLinus Torvalds #include <net/sock.h> 511da177e4SLinus Torvalds #include <net/ip_fib.h> 521da177e4SLinus Torvalds 531da177e4SLinus Torvalds #define FRprintk(a...) 541da177e4SLinus Torvalds 551da177e4SLinus Torvalds struct fib_rule 561da177e4SLinus Torvalds { 577b204afdSRobert Olsson struct hlist_node hlist; 581da177e4SLinus Torvalds atomic_t r_clntref; 591da177e4SLinus Torvalds u32 r_preference; 601da177e4SLinus Torvalds unsigned char r_table; 611da177e4SLinus Torvalds unsigned char r_action; 621da177e4SLinus Torvalds unsigned char r_dst_len; 631da177e4SLinus Torvalds unsigned char r_src_len; 641da177e4SLinus Torvalds u32 r_src; 651da177e4SLinus Torvalds u32 r_srcmask; 661da177e4SLinus Torvalds u32 r_dst; 671da177e4SLinus Torvalds u32 r_dstmask; 681da177e4SLinus Torvalds u32 r_srcmap; 691da177e4SLinus Torvalds u8 r_flags; 701da177e4SLinus Torvalds u8 r_tos; 711da177e4SLinus Torvalds #ifdef CONFIG_IP_ROUTE_FWMARK 721da177e4SLinus Torvalds u32 r_fwmark; 731da177e4SLinus Torvalds #endif 741da177e4SLinus Torvalds int r_ifindex; 751da177e4SLinus Torvalds #ifdef CONFIG_NET_CLS_ROUTE 761da177e4SLinus Torvalds __u32 r_tclassid; 771da177e4SLinus Torvalds #endif 781da177e4SLinus Torvalds char r_ifname[IFNAMSIZ]; 791da177e4SLinus Torvalds int r_dead; 807b204afdSRobert Olsson struct rcu_head rcu; 811da177e4SLinus Torvalds }; 821da177e4SLinus Torvalds 831da177e4SLinus Torvalds static struct fib_rule default_rule = { 841da177e4SLinus Torvalds .r_clntref = ATOMIC_INIT(2), 851da177e4SLinus Torvalds .r_preference = 0x7FFF, 861da177e4SLinus Torvalds .r_table = RT_TABLE_DEFAULT, 871da177e4SLinus Torvalds .r_action = RTN_UNICAST, 881da177e4SLinus Torvalds }; 891da177e4SLinus Torvalds 901da177e4SLinus Torvalds static struct fib_rule main_rule = { 911da177e4SLinus Torvalds .r_clntref = ATOMIC_INIT(2), 921da177e4SLinus Torvalds .r_preference = 0x7FFE, 931da177e4SLinus Torvalds .r_table = RT_TABLE_MAIN, 941da177e4SLinus Torvalds .r_action = RTN_UNICAST, 951da177e4SLinus Torvalds }; 961da177e4SLinus Torvalds 971da177e4SLinus Torvalds static struct fib_rule local_rule = { 981da177e4SLinus Torvalds .r_clntref = ATOMIC_INIT(2), 991da177e4SLinus Torvalds .r_table = RT_TABLE_LOCAL, 1001da177e4SLinus Torvalds .r_action = RTN_UNICAST, 1011da177e4SLinus Torvalds }; 1021da177e4SLinus Torvalds 103d15150f7SAdrian Bunk static struct hlist_head fib_rules; 1047b204afdSRobert Olsson 1057b204afdSRobert Olsson /* writer func called from netlink -- rtnl_sem hold*/ 1061da177e4SLinus Torvalds 107*a5cdc030SPatrick McHardy static void rtmsg_rule(int, struct fib_rule *); 108*a5cdc030SPatrick McHardy 1091da177e4SLinus Torvalds int inet_rtm_delrule(struct sk_buff *skb, struct nlmsghdr* nlh, void *arg) 1101da177e4SLinus Torvalds { 1111da177e4SLinus Torvalds struct rtattr **rta = arg; 1121da177e4SLinus Torvalds struct rtmsg *rtm = NLMSG_DATA(nlh); 1137b204afdSRobert Olsson struct fib_rule *r; 1147b204afdSRobert Olsson struct hlist_node *node; 1151da177e4SLinus Torvalds int err = -ESRCH; 1161da177e4SLinus Torvalds 1177b204afdSRobert Olsson hlist_for_each_entry(r, node, &fib_rules, hlist) { 1181da177e4SLinus Torvalds if ((!rta[RTA_SRC-1] || memcmp(RTA_DATA(rta[RTA_SRC-1]), &r->r_src, 4) == 0) && 1191da177e4SLinus Torvalds rtm->rtm_src_len == r->r_src_len && 1201da177e4SLinus Torvalds rtm->rtm_dst_len == r->r_dst_len && 1211da177e4SLinus Torvalds (!rta[RTA_DST-1] || memcmp(RTA_DATA(rta[RTA_DST-1]), &r->r_dst, 4) == 0) && 1221da177e4SLinus Torvalds rtm->rtm_tos == r->r_tos && 1231da177e4SLinus Torvalds #ifdef CONFIG_IP_ROUTE_FWMARK 1241da177e4SLinus Torvalds (!rta[RTA_PROTOINFO-1] || memcmp(RTA_DATA(rta[RTA_PROTOINFO-1]), &r->r_fwmark, 4) == 0) && 1251da177e4SLinus Torvalds #endif 1261da177e4SLinus Torvalds (!rtm->rtm_type || rtm->rtm_type == r->r_action) && 1271da177e4SLinus Torvalds (!rta[RTA_PRIORITY-1] || memcmp(RTA_DATA(rta[RTA_PRIORITY-1]), &r->r_preference, 4) == 0) && 1281da177e4SLinus Torvalds (!rta[RTA_IIF-1] || rtattr_strcmp(rta[RTA_IIF-1], r->r_ifname) == 0) && 1291da177e4SLinus Torvalds (!rtm->rtm_table || (r && rtm->rtm_table == r->r_table))) { 1301da177e4SLinus Torvalds err = -EPERM; 1311da177e4SLinus Torvalds if (r == &local_rule) 1321da177e4SLinus Torvalds break; 1331da177e4SLinus Torvalds 1347b204afdSRobert Olsson hlist_del_rcu(&r->hlist); 1351da177e4SLinus Torvalds r->r_dead = 1; 136*a5cdc030SPatrick McHardy rtmsg_rule(RTM_DELRULE, r); 1371da177e4SLinus Torvalds fib_rule_put(r); 1381da177e4SLinus Torvalds err = 0; 1391da177e4SLinus Torvalds break; 1401da177e4SLinus Torvalds } 1411da177e4SLinus Torvalds } 1421da177e4SLinus Torvalds return err; 1431da177e4SLinus Torvalds } 1441da177e4SLinus Torvalds 1451da177e4SLinus Torvalds /* Allocate new unique table id */ 1461da177e4SLinus Torvalds 1471da177e4SLinus Torvalds static struct fib_table *fib_empty_table(void) 1481da177e4SLinus Torvalds { 1491da177e4SLinus Torvalds int id; 1501da177e4SLinus Torvalds 1511da177e4SLinus Torvalds for (id = 1; id <= RT_TABLE_MAX; id++) 1521da177e4SLinus Torvalds if (fib_tables[id] == NULL) 1531da177e4SLinus Torvalds return __fib_new_table(id); 1541da177e4SLinus Torvalds return NULL; 1551da177e4SLinus Torvalds } 1561da177e4SLinus Torvalds 1577b204afdSRobert Olsson static inline void fib_rule_put_rcu(struct rcu_head *head) 1587b204afdSRobert Olsson { 1597b204afdSRobert Olsson struct fib_rule *r = container_of(head, struct fib_rule, rcu); 1607b204afdSRobert Olsson kfree(r); 1617b204afdSRobert Olsson } 1627b204afdSRobert Olsson 1631da177e4SLinus Torvalds void fib_rule_put(struct fib_rule *r) 1641da177e4SLinus Torvalds { 1651da177e4SLinus Torvalds if (atomic_dec_and_test(&r->r_clntref)) { 1661da177e4SLinus Torvalds if (r->r_dead) 1677b204afdSRobert Olsson call_rcu(&r->rcu, fib_rule_put_rcu); 1681da177e4SLinus Torvalds else 1691da177e4SLinus Torvalds printk("Freeing alive rule %p\n", r); 1701da177e4SLinus Torvalds } 1711da177e4SLinus Torvalds } 1721da177e4SLinus Torvalds 1737b204afdSRobert Olsson /* writer func called from netlink -- rtnl_sem hold*/ 1747b204afdSRobert Olsson 1751da177e4SLinus Torvalds int inet_rtm_newrule(struct sk_buff *skb, struct nlmsghdr* nlh, void *arg) 1761da177e4SLinus Torvalds { 1771da177e4SLinus Torvalds struct rtattr **rta = arg; 1781da177e4SLinus Torvalds struct rtmsg *rtm = NLMSG_DATA(nlh); 1797b204afdSRobert Olsson struct fib_rule *r, *new_r, *last = NULL; 1807b204afdSRobert Olsson struct hlist_node *node = NULL; 1811da177e4SLinus Torvalds unsigned char table_id; 1821da177e4SLinus Torvalds 1831da177e4SLinus Torvalds if (rtm->rtm_src_len > 32 || rtm->rtm_dst_len > 32 || 1841da177e4SLinus Torvalds (rtm->rtm_tos & ~IPTOS_TOS_MASK)) 1851da177e4SLinus Torvalds return -EINVAL; 1861da177e4SLinus Torvalds 1871da177e4SLinus Torvalds if (rta[RTA_IIF-1] && RTA_PAYLOAD(rta[RTA_IIF-1]) > IFNAMSIZ) 1881da177e4SLinus Torvalds return -EINVAL; 1891da177e4SLinus Torvalds 1901da177e4SLinus Torvalds table_id = rtm->rtm_table; 1911da177e4SLinus Torvalds if (table_id == RT_TABLE_UNSPEC) { 1921da177e4SLinus Torvalds struct fib_table *table; 1931da177e4SLinus Torvalds if (rtm->rtm_type == RTN_UNICAST) { 1941da177e4SLinus Torvalds if ((table = fib_empty_table()) == NULL) 1951da177e4SLinus Torvalds return -ENOBUFS; 1961da177e4SLinus Torvalds table_id = table->tb_id; 1971da177e4SLinus Torvalds } 1981da177e4SLinus Torvalds } 1991da177e4SLinus Torvalds 2001da177e4SLinus Torvalds new_r = kmalloc(sizeof(*new_r), GFP_KERNEL); 2011da177e4SLinus Torvalds if (!new_r) 2021da177e4SLinus Torvalds return -ENOMEM; 2031da177e4SLinus Torvalds memset(new_r, 0, sizeof(*new_r)); 2047b204afdSRobert Olsson 2051da177e4SLinus Torvalds if (rta[RTA_SRC-1]) 2061da177e4SLinus Torvalds memcpy(&new_r->r_src, RTA_DATA(rta[RTA_SRC-1]), 4); 2071da177e4SLinus Torvalds if (rta[RTA_DST-1]) 2081da177e4SLinus Torvalds memcpy(&new_r->r_dst, RTA_DATA(rta[RTA_DST-1]), 4); 2091da177e4SLinus Torvalds if (rta[RTA_GATEWAY-1]) 2101da177e4SLinus Torvalds memcpy(&new_r->r_srcmap, RTA_DATA(rta[RTA_GATEWAY-1]), 4); 2111da177e4SLinus Torvalds new_r->r_src_len = rtm->rtm_src_len; 2121da177e4SLinus Torvalds new_r->r_dst_len = rtm->rtm_dst_len; 2131da177e4SLinus Torvalds new_r->r_srcmask = inet_make_mask(rtm->rtm_src_len); 2141da177e4SLinus Torvalds new_r->r_dstmask = inet_make_mask(rtm->rtm_dst_len); 2151da177e4SLinus Torvalds new_r->r_tos = rtm->rtm_tos; 2161da177e4SLinus Torvalds #ifdef CONFIG_IP_ROUTE_FWMARK 2171da177e4SLinus Torvalds if (rta[RTA_PROTOINFO-1]) 2181da177e4SLinus Torvalds memcpy(&new_r->r_fwmark, RTA_DATA(rta[RTA_PROTOINFO-1]), 4); 2191da177e4SLinus Torvalds #endif 2201da177e4SLinus Torvalds new_r->r_action = rtm->rtm_type; 2211da177e4SLinus Torvalds new_r->r_flags = rtm->rtm_flags; 2221da177e4SLinus Torvalds if (rta[RTA_PRIORITY-1]) 2231da177e4SLinus Torvalds memcpy(&new_r->r_preference, RTA_DATA(rta[RTA_PRIORITY-1]), 4); 2241da177e4SLinus Torvalds new_r->r_table = table_id; 2251da177e4SLinus Torvalds if (rta[RTA_IIF-1]) { 2261da177e4SLinus Torvalds struct net_device *dev; 2271da177e4SLinus Torvalds rtattr_strlcpy(new_r->r_ifname, rta[RTA_IIF-1], IFNAMSIZ); 2281da177e4SLinus Torvalds new_r->r_ifindex = -1; 2291da177e4SLinus Torvalds dev = __dev_get_by_name(new_r->r_ifname); 2301da177e4SLinus Torvalds if (dev) 2311da177e4SLinus Torvalds new_r->r_ifindex = dev->ifindex; 2321da177e4SLinus Torvalds } 2331da177e4SLinus Torvalds #ifdef CONFIG_NET_CLS_ROUTE 2341da177e4SLinus Torvalds if (rta[RTA_FLOW-1]) 2351da177e4SLinus Torvalds memcpy(&new_r->r_tclassid, RTA_DATA(rta[RTA_FLOW-1]), 4); 2361da177e4SLinus Torvalds #endif 2377b204afdSRobert Olsson r = container_of(fib_rules.first, struct fib_rule, hlist); 2381da177e4SLinus Torvalds 2391da177e4SLinus Torvalds if (!new_r->r_preference) { 2407b204afdSRobert Olsson if (r && r->hlist.next != NULL) { 2417b204afdSRobert Olsson r = container_of(r->hlist.next, struct fib_rule, hlist); 2421da177e4SLinus Torvalds if (r->r_preference) 2431da177e4SLinus Torvalds new_r->r_preference = r->r_preference - 1; 2441da177e4SLinus Torvalds } 2451da177e4SLinus Torvalds } 2461da177e4SLinus Torvalds 2477b204afdSRobert Olsson hlist_for_each_entry(r, node, &fib_rules, hlist) { 2481da177e4SLinus Torvalds if (r->r_preference > new_r->r_preference) 2491da177e4SLinus Torvalds break; 2507b204afdSRobert Olsson last = r; 2511da177e4SLinus Torvalds } 2521da177e4SLinus Torvalds atomic_inc(&new_r->r_clntref); 2537b204afdSRobert Olsson 2547b204afdSRobert Olsson if (last) 2557b204afdSRobert Olsson hlist_add_after_rcu(&last->hlist, &new_r->hlist); 2567b204afdSRobert Olsson else 2577b204afdSRobert Olsson hlist_add_before_rcu(&new_r->hlist, &r->hlist); 2587b204afdSRobert Olsson 259*a5cdc030SPatrick McHardy rtmsg_rule(RTM_NEWRULE, new_r); 2601da177e4SLinus Torvalds return 0; 2611da177e4SLinus Torvalds } 2621da177e4SLinus Torvalds 2631da177e4SLinus Torvalds #ifdef CONFIG_NET_CLS_ROUTE 2641da177e4SLinus Torvalds u32 fib_rules_tclass(struct fib_result *res) 2651da177e4SLinus Torvalds { 2661da177e4SLinus Torvalds if (res->r) 2671da177e4SLinus Torvalds return res->r->r_tclassid; 2681da177e4SLinus Torvalds return 0; 2691da177e4SLinus Torvalds } 2701da177e4SLinus Torvalds #endif 2711da177e4SLinus Torvalds 2727b204afdSRobert Olsson /* callers should hold rtnl semaphore */ 2731da177e4SLinus Torvalds 2741da177e4SLinus Torvalds static void fib_rules_detach(struct net_device *dev) 2751da177e4SLinus Torvalds { 2767b204afdSRobert Olsson struct hlist_node *node; 2771da177e4SLinus Torvalds struct fib_rule *r; 2781da177e4SLinus Torvalds 2797b204afdSRobert Olsson hlist_for_each_entry(r, node, &fib_rules, hlist) { 2807b204afdSRobert Olsson if (r->r_ifindex == dev->ifindex) 2811da177e4SLinus Torvalds r->r_ifindex = -1; 2827b204afdSRobert Olsson 2831da177e4SLinus Torvalds } 2841da177e4SLinus Torvalds } 2857b204afdSRobert Olsson 2867b204afdSRobert Olsson /* callers should hold rtnl semaphore */ 2871da177e4SLinus Torvalds 2881da177e4SLinus Torvalds static void fib_rules_attach(struct net_device *dev) 2891da177e4SLinus Torvalds { 2907b204afdSRobert Olsson struct hlist_node *node; 2911da177e4SLinus Torvalds struct fib_rule *r; 2921da177e4SLinus Torvalds 2937b204afdSRobert Olsson hlist_for_each_entry(r, node, &fib_rules, hlist) { 2947b204afdSRobert Olsson if (r->r_ifindex == -1 && strcmp(dev->name, r->r_ifname) == 0) 2951da177e4SLinus Torvalds r->r_ifindex = dev->ifindex; 2961da177e4SLinus Torvalds } 2971da177e4SLinus Torvalds } 2981da177e4SLinus Torvalds 2991da177e4SLinus Torvalds int fib_lookup(const struct flowi *flp, struct fib_result *res) 3001da177e4SLinus Torvalds { 3011da177e4SLinus Torvalds int err; 3021da177e4SLinus Torvalds struct fib_rule *r, *policy; 3031da177e4SLinus Torvalds struct fib_table *tb; 3047b204afdSRobert Olsson struct hlist_node *node; 3051da177e4SLinus Torvalds 3061da177e4SLinus Torvalds u32 daddr = flp->fl4_dst; 3071da177e4SLinus Torvalds u32 saddr = flp->fl4_src; 3081da177e4SLinus Torvalds 3091da177e4SLinus Torvalds FRprintk("Lookup: %u.%u.%u.%u <- %u.%u.%u.%u ", 3101da177e4SLinus Torvalds NIPQUAD(flp->fl4_dst), NIPQUAD(flp->fl4_src)); 3117b204afdSRobert Olsson 3127b204afdSRobert Olsson rcu_read_lock(); 3137b204afdSRobert Olsson 3147b204afdSRobert Olsson hlist_for_each_entry_rcu(r, node, &fib_rules, hlist) { 3151da177e4SLinus Torvalds if (((saddr^r->r_src) & r->r_srcmask) || 3161da177e4SLinus Torvalds ((daddr^r->r_dst) & r->r_dstmask) || 3171da177e4SLinus Torvalds (r->r_tos && r->r_tos != flp->fl4_tos) || 3181da177e4SLinus Torvalds #ifdef CONFIG_IP_ROUTE_FWMARK 3191da177e4SLinus Torvalds (r->r_fwmark && r->r_fwmark != flp->fl4_fwmark) || 3201da177e4SLinus Torvalds #endif 3211da177e4SLinus Torvalds (r->r_ifindex && r->r_ifindex != flp->iif)) 3221da177e4SLinus Torvalds continue; 3231da177e4SLinus Torvalds 3241da177e4SLinus Torvalds FRprintk("tb %d r %d ", r->r_table, r->r_action); 3251da177e4SLinus Torvalds switch (r->r_action) { 3261da177e4SLinus Torvalds case RTN_UNICAST: 3271da177e4SLinus Torvalds policy = r; 3281da177e4SLinus Torvalds break; 3291da177e4SLinus Torvalds case RTN_UNREACHABLE: 3307b204afdSRobert Olsson rcu_read_unlock(); 3311da177e4SLinus Torvalds return -ENETUNREACH; 3321da177e4SLinus Torvalds default: 3331da177e4SLinus Torvalds case RTN_BLACKHOLE: 3347b204afdSRobert Olsson rcu_read_unlock(); 3351da177e4SLinus Torvalds return -EINVAL; 3361da177e4SLinus Torvalds case RTN_PROHIBIT: 3377b204afdSRobert Olsson rcu_read_unlock(); 3381da177e4SLinus Torvalds return -EACCES; 3391da177e4SLinus Torvalds } 3401da177e4SLinus Torvalds 3411da177e4SLinus Torvalds if ((tb = fib_get_table(r->r_table)) == NULL) 3421da177e4SLinus Torvalds continue; 3431da177e4SLinus Torvalds err = tb->tb_lookup(tb, flp, res); 3441da177e4SLinus Torvalds if (err == 0) { 3451da177e4SLinus Torvalds res->r = policy; 3461da177e4SLinus Torvalds if (policy) 3471da177e4SLinus Torvalds atomic_inc(&policy->r_clntref); 3487b204afdSRobert Olsson rcu_read_unlock(); 3491da177e4SLinus Torvalds return 0; 3501da177e4SLinus Torvalds } 3511da177e4SLinus Torvalds if (err < 0 && err != -EAGAIN) { 3527b204afdSRobert Olsson rcu_read_unlock(); 3531da177e4SLinus Torvalds return err; 3541da177e4SLinus Torvalds } 3551da177e4SLinus Torvalds } 3561da177e4SLinus Torvalds FRprintk("FAILURE\n"); 3577b204afdSRobert Olsson rcu_read_unlock(); 3581da177e4SLinus Torvalds return -ENETUNREACH; 3591da177e4SLinus Torvalds } 3601da177e4SLinus Torvalds 3611da177e4SLinus Torvalds void fib_select_default(const struct flowi *flp, struct fib_result *res) 3621da177e4SLinus Torvalds { 3631da177e4SLinus Torvalds if (res->r && res->r->r_action == RTN_UNICAST && 3641da177e4SLinus Torvalds FIB_RES_GW(*res) && FIB_RES_NH(*res).nh_scope == RT_SCOPE_LINK) { 3651da177e4SLinus Torvalds struct fib_table *tb; 3661da177e4SLinus Torvalds if ((tb = fib_get_table(res->r->r_table)) != NULL) 3671da177e4SLinus Torvalds tb->tb_select_default(tb, flp, res); 3681da177e4SLinus Torvalds } 3691da177e4SLinus Torvalds } 3701da177e4SLinus Torvalds 3711da177e4SLinus Torvalds static int fib_rules_event(struct notifier_block *this, unsigned long event, void *ptr) 3721da177e4SLinus Torvalds { 3731da177e4SLinus Torvalds struct net_device *dev = ptr; 3741da177e4SLinus Torvalds 3751da177e4SLinus Torvalds if (event == NETDEV_UNREGISTER) 3761da177e4SLinus Torvalds fib_rules_detach(dev); 3771da177e4SLinus Torvalds else if (event == NETDEV_REGISTER) 3781da177e4SLinus Torvalds fib_rules_attach(dev); 3791da177e4SLinus Torvalds return NOTIFY_DONE; 3801da177e4SLinus Torvalds } 3811da177e4SLinus Torvalds 3821da177e4SLinus Torvalds 3831da177e4SLinus Torvalds static struct notifier_block fib_rules_notifier = { 3841da177e4SLinus Torvalds .notifier_call =fib_rules_event, 3851da177e4SLinus Torvalds }; 3861da177e4SLinus Torvalds 3871da177e4SLinus Torvalds static __inline__ int inet_fill_rule(struct sk_buff *skb, 3881da177e4SLinus Torvalds struct fib_rule *r, 389*a5cdc030SPatrick McHardy u32 pid, u32 seq, int event, 390b6544c0bSJamal Hadi Salim unsigned int flags) 3911da177e4SLinus Torvalds { 3921da177e4SLinus Torvalds struct rtmsg *rtm; 3931da177e4SLinus Torvalds struct nlmsghdr *nlh; 3941da177e4SLinus Torvalds unsigned char *b = skb->tail; 3951da177e4SLinus Torvalds 396*a5cdc030SPatrick McHardy nlh = NLMSG_NEW(skb, pid, seq, event, sizeof(*rtm), flags); 3971da177e4SLinus Torvalds rtm = NLMSG_DATA(nlh); 3981da177e4SLinus Torvalds rtm->rtm_family = AF_INET; 3991da177e4SLinus Torvalds rtm->rtm_dst_len = r->r_dst_len; 4001da177e4SLinus Torvalds rtm->rtm_src_len = r->r_src_len; 4011da177e4SLinus Torvalds rtm->rtm_tos = r->r_tos; 4021da177e4SLinus Torvalds #ifdef CONFIG_IP_ROUTE_FWMARK 4031da177e4SLinus Torvalds if (r->r_fwmark) 4041da177e4SLinus Torvalds RTA_PUT(skb, RTA_PROTOINFO, 4, &r->r_fwmark); 4051da177e4SLinus Torvalds #endif 4061da177e4SLinus Torvalds rtm->rtm_table = r->r_table; 4071da177e4SLinus Torvalds rtm->rtm_protocol = 0; 4081da177e4SLinus Torvalds rtm->rtm_scope = 0; 4091da177e4SLinus Torvalds rtm->rtm_type = r->r_action; 4101da177e4SLinus Torvalds rtm->rtm_flags = r->r_flags; 4111da177e4SLinus Torvalds 4121da177e4SLinus Torvalds if (r->r_dst_len) 4131da177e4SLinus Torvalds RTA_PUT(skb, RTA_DST, 4, &r->r_dst); 4141da177e4SLinus Torvalds if (r->r_src_len) 4151da177e4SLinus Torvalds RTA_PUT(skb, RTA_SRC, 4, &r->r_src); 4161da177e4SLinus Torvalds if (r->r_ifname[0]) 4171da177e4SLinus Torvalds RTA_PUT(skb, RTA_IIF, IFNAMSIZ, &r->r_ifname); 4181da177e4SLinus Torvalds if (r->r_preference) 4191da177e4SLinus Torvalds RTA_PUT(skb, RTA_PRIORITY, 4, &r->r_preference); 4201da177e4SLinus Torvalds if (r->r_srcmap) 4211da177e4SLinus Torvalds RTA_PUT(skb, RTA_GATEWAY, 4, &r->r_srcmap); 4221da177e4SLinus Torvalds #ifdef CONFIG_NET_CLS_ROUTE 4231da177e4SLinus Torvalds if (r->r_tclassid) 4241da177e4SLinus Torvalds RTA_PUT(skb, RTA_FLOW, 4, &r->r_tclassid); 4251da177e4SLinus Torvalds #endif 4261da177e4SLinus Torvalds nlh->nlmsg_len = skb->tail - b; 4271da177e4SLinus Torvalds return skb->len; 4281da177e4SLinus Torvalds 4291da177e4SLinus Torvalds nlmsg_failure: 4301da177e4SLinus Torvalds rtattr_failure: 4311da177e4SLinus Torvalds skb_trim(skb, b - skb->data); 4321da177e4SLinus Torvalds return -1; 4331da177e4SLinus Torvalds } 4341da177e4SLinus Torvalds 4357b204afdSRobert Olsson /* callers should hold rtnl semaphore */ 4367b204afdSRobert Olsson 437*a5cdc030SPatrick McHardy static void rtmsg_rule(int event, struct fib_rule *r) 438*a5cdc030SPatrick McHardy { 439*a5cdc030SPatrick McHardy int size = NLMSG_SPACE(sizeof(struct rtmsg) + 128); 440*a5cdc030SPatrick McHardy struct sk_buff *skb = alloc_skb(size, GFP_KERNEL); 441*a5cdc030SPatrick McHardy 442*a5cdc030SPatrick McHardy if (!skb) 443*a5cdc030SPatrick McHardy netlink_set_err(rtnl, 0, RTNLGRP_IPV4_RULE, ENOBUFS); 444*a5cdc030SPatrick McHardy else if (inet_fill_rule(skb, r, 0, 0, event, 0) < 0) { 445*a5cdc030SPatrick McHardy kfree_skb(skb); 446*a5cdc030SPatrick McHardy netlink_set_err(rtnl, 0, RTNLGRP_IPV4_RULE, EINVAL); 447*a5cdc030SPatrick McHardy } else { 448*a5cdc030SPatrick McHardy netlink_broadcast(rtnl, skb, 0, RTNLGRP_IPV4_RULE, GFP_KERNEL); 449*a5cdc030SPatrick McHardy } 450*a5cdc030SPatrick McHardy } 451*a5cdc030SPatrick McHardy 4521da177e4SLinus Torvalds int inet_dump_rules(struct sk_buff *skb, struct netlink_callback *cb) 4531da177e4SLinus Torvalds { 4547b204afdSRobert Olsson int idx = 0; 4551da177e4SLinus Torvalds int s_idx = cb->args[0]; 4561da177e4SLinus Torvalds struct fib_rule *r; 4577b204afdSRobert Olsson struct hlist_node *node; 4581da177e4SLinus Torvalds 4597b204afdSRobert Olsson rcu_read_lock(); 4607b204afdSRobert Olsson hlist_for_each_entry(r, node, &fib_rules, hlist) { 4617b204afdSRobert Olsson 4621da177e4SLinus Torvalds if (idx < s_idx) 4631da177e4SLinus Torvalds continue; 464*a5cdc030SPatrick McHardy if (inet_fill_rule(skb, r, NETLINK_CB(cb->skb).pid, 465*a5cdc030SPatrick McHardy cb->nlh->nlmsg_seq, 466*a5cdc030SPatrick McHardy RTM_NEWRULE, NLM_F_MULTI) < 0) 4671da177e4SLinus Torvalds break; 4687b204afdSRobert Olsson idx++; 4691da177e4SLinus Torvalds } 4707b204afdSRobert Olsson rcu_read_unlock(); 4711da177e4SLinus Torvalds cb->args[0] = idx; 4721da177e4SLinus Torvalds 4731da177e4SLinus Torvalds return skb->len; 4741da177e4SLinus Torvalds } 4751da177e4SLinus Torvalds 4761da177e4SLinus Torvalds void __init fib_rules_init(void) 4771da177e4SLinus Torvalds { 4787b204afdSRobert Olsson INIT_HLIST_HEAD(&fib_rules); 4797b204afdSRobert Olsson hlist_add_head(&local_rule.hlist, &fib_rules); 4807b204afdSRobert Olsson hlist_add_after(&local_rule.hlist, &main_rule.hlist); 4817b204afdSRobert Olsson hlist_add_after(&main_rule.hlist, &default_rule.hlist); 4821da177e4SLinus Torvalds register_netdevice_notifier(&fib_rules_notifier); 4831da177e4SLinus Torvalds } 484