/*
* 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: FIB frontend.
*
* Version: $Id: fib_frontend.c,v 1.26 2001/10/31 21:55:54 davem Exp $
*
* Authors: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
*
* 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.
*/
#include <linux/module.h>
#include <asm/uaccess.h>
#include <asm/system.h>
#include <linux/bitops.h>
#include <linux/capability.h>
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/mm.h>
#include <linux/string.h>
#include <linux/socket.h>
#include <linux/sockios.h>
#include <linux/errno.h>
#include <linux/in.h>
#include <linux/inet.h>
#include <linux/inetdevice.h>
#include <linux/netdevice.h>
#include <linux/if_addr.h>
#include <linux/if_arp.h>
#include <linux/skbuff.h>
#include <linux/init.h>
#include <linux/list.h>
#include <net/ip.h>
#include <net/protocol.h>
#include <net/route.h>
#include <net/tcp.h>
#include <net/sock.h>
#include <net/icmp.h>
#include <net/arp.h>
#include <net/ip_fib.h>
#include <net/rtnetlink.h>
#define FFprint(a...) printk(KERN_DEBUG a)
#ifndef CONFIG_IP_MULTIPLE_TABLES
struct fib_table *ip_fib_local_table;
struct fib_table *ip_fib_main_table;
#define FIB_TABLE_HASHSZ 1
static struct hlist_head fib_table_hash[FIB_TABLE_HASHSZ];
#else
#define FIB_TABLE_HASHSZ 256
static struct hlist_head fib_table_hash[FIB_TABLE_HASHSZ];
struct fib_table *fib_new_table(u32 id)
{
struct fib_table *tb;
unsigned int h;
if (id == 0)
id = RT_TABLE_MAIN;
tb = fib_get_table(id);
if (tb)
return tb;
tb = fib_hash_init(id);
if (!tb)
return NULL;
h = id & (FIB_TABLE_HASHSZ - 1);
hlist_add_head_rcu(&tb->tb_hlist, &fib_table_hash[h]);
return tb;
}
struct fib_table *fib_get_table(u32 id)
{
struct fib_table *tb;
struct hlist_node *node;
unsigned int h;
if (id == 0)
id = RT_TABLE_MAIN;
h = id & (FIB_TABLE_HASHSZ - 1);
rcu_read_lock();
hlist_for_each_entry_rcu(tb, node, &fib_table_hash[h], tb_hlist) {
if (tb->tb_id == id) {
rcu_read_unlock();
return tb;
}
}
rcu_read_unlock();
return NULL;
}
#endif /* CONFIG_IP_MULTIPLE_TABLES */
static void fib_flush(void)
{
int flushed = 0;
struct fib_table *tb;
struct hlist_node *node;
unsigned int h;
for (h = 0; h < FIB_TABLE_HASHSZ; h++) {
hlist_for_each_entry(tb, node, &fib_table_hash[h], tb_hlist)
flushed += tb->tb_flush(tb);
}
if (flushed)
rt_cache_flush(-1);
}
/*
* Find the first device with a given source address.
*/
struct net_device * ip_dev_find(__be32 addr)
{
struct flowi fl = { .nl_u = { .ip4_u = { .daddr = addr } } };
struct fib_result res;
struct net_device *dev = NULL;
#ifdef CONFIG_IP_MULTIPLE_TABLES
res.r = NULL;
#endif
if (!ip_fib_local_table ||
ip_fib_local_table->tb_lookup(ip_fib_local_table, &fl, &res))
return NULL;
if (res.type != RTN_LOCAL)
goto out;
dev = FIB_RES_DEV(res);
if (dev)
dev_hold(dev);
out:
fib_res_put(&res);
return dev;
}
unsigned inet_addr_type(__be32 addr)
{
struct flowi fl = { .nl_u = { .ip4_u = { .daddr = addr } } };
struct fib_result res;
unsigned ret = RTN_BROADCAST;
if (ZERONET(addr) || BADCLASS(addr))
return RTN_BROADCAST;
if (MULTICAST(addr))
return RTN_MULTICAST;
#ifdef CONFIG_IP_MULTIPLE_TABLES
res.r = NULL;
#endif
if (ip_fib_local_table) {
ret = RTN_UNICAST;
if (!ip_fib_local_table->tb_lookup(ip_fib_local_table,
&fl, &res)) {
ret = res.type;
fib_res_put(&res);
}
}
return ret;
}
/* Given (packet source, input interface) and optional (dst, oif, tos):
- (main) check, that source is valid i.e. not broadcast or our local
address.
- figure out what "logical" interface this packet arrived
and calculate "specific destination" address.
- check, that packet arrived from expected physical interface.
*/
int fib_validate_source(__be32 src, __be32 dst, u8 tos, int oif,
struct net_device *dev, __be32 *spec_dst, u32 *itag)
{
struct in_device *in_dev;
struct flowi fl = { .nl_u = { .