535 lines
9.5 KiB
C
535 lines
9.5 KiB
C
/**
|
|
* \brief Linux emulation code
|
|
* \author Sebastian Sumpf
|
|
* \author Josef Soentgen
|
|
* \date 2013-08-30
|
|
*/
|
|
|
|
/*
|
|
* Copyright (C) 2013-2016 Genode Labs GmbH
|
|
*
|
|
* This file is part of the Genode OS framework, which is distributed
|
|
* under the terms of the GNU General Public License version 2.
|
|
*/
|
|
|
|
/* Linux includes */
|
|
#include <linux/inetdevice.h>
|
|
#include <linux/net.h>
|
|
#include <linux/netdevice.h>
|
|
#include <uapi/linux/rtnetlink.h>
|
|
#include <net/sock.h>
|
|
#include <net/route.h>
|
|
#include <net/ip_fib.h>
|
|
#include <net/tcp.h>
|
|
#include <net/tcp_states.h>
|
|
|
|
|
|
/***********************
|
|
** linux/genetlink.h **
|
|
***********************/
|
|
|
|
/* needed by af_netlink.c */
|
|
atomic_t genl_sk_destructing_cnt;
|
|
wait_queue_head_t genl_sk_destructing_waitq;
|
|
|
|
|
|
/****************************
|
|
** asm-generic/atomic64.h **
|
|
****************************/
|
|
|
|
long long atomic64_read(const atomic64_t *v)
|
|
{
|
|
return v->counter;
|
|
}
|
|
|
|
|
|
void atomic64_set(atomic64_t *v, long long i)
|
|
{
|
|
v->counter = i;
|
|
}
|
|
|
|
|
|
/********************
|
|
** linux/bitmap.h **
|
|
********************/
|
|
|
|
void bitmap_fill(unsigned long *dst, int nbits)
|
|
{
|
|
int count = nbits / (8 * sizeof (long));
|
|
int i;
|
|
|
|
memset(dst, 0xff, count * sizeof(long));
|
|
dst += count;
|
|
|
|
count = nbits % (8 * sizeof(long));
|
|
|
|
for (i = 0; i < count; i++)
|
|
*dst |= (1 << i);
|
|
}
|
|
|
|
|
|
void bitmap_zero(unsigned long *dst, int nbits)
|
|
{
|
|
int len = BITS_TO_LONGS(nbits) * sizeof(unsigned long);
|
|
memset(dst, 0, len);
|
|
}
|
|
|
|
|
|
/*****************
|
|
** linux/gfp.h **
|
|
*****************/
|
|
|
|
unsigned long get_zeroed_page(gfp_t gfp_mask)
|
|
{
|
|
return (unsigned long)kzalloc(PAGE_SIZE, 0);
|
|
}
|
|
|
|
|
|
/********************
|
|
** linux/percpu.h **
|
|
********************/
|
|
|
|
void *__alloc_percpu(size_t size, size_t align)
|
|
{
|
|
return kmalloc(size, 0);
|
|
}
|
|
|
|
|
|
/******************
|
|
** linux/hash.h **
|
|
******************/
|
|
|
|
u32 hash_32(u32 val, unsigned int bits)
|
|
{
|
|
enum { GOLDEN_RATIO_PRIME_32 = 0x9e370001UL };
|
|
u32 hash = val * GOLDEN_RATIO_PRIME_32;
|
|
|
|
hash = hash >> (32 - bits);
|
|
return hash;
|
|
}
|
|
|
|
|
|
/******************
|
|
** linux/dcache **
|
|
******************/
|
|
|
|
unsigned int full_name_hash(const unsigned char *name, unsigned int len)
|
|
{
|
|
unsigned hash = 0, i;
|
|
for (i = 0; i < len; i++)
|
|
hash += name[i];
|
|
|
|
return hash;
|
|
}
|
|
|
|
|
|
/******************************
|
|
* net/core/net/namespace.h **
|
|
******************************/
|
|
|
|
int register_pernet_subsys(struct pernet_operations *ops)
|
|
{
|
|
if (ops->init)
|
|
ops->init(&init_net);
|
|
|
|
return 0;
|
|
}
|
|
|
|
int register_pernet_device(struct pernet_operations *ops)
|
|
{
|
|
return register_pernet_subsys(ops);
|
|
}
|
|
|
|
|
|
/*************************
|
|
** net/net_namespace.h **
|
|
*************************/
|
|
|
|
int rt_genid(struct net *net)
|
|
{
|
|
int ret = atomic_read(&net->rt_genid);
|
|
return -1;
|
|
}
|
|
|
|
|
|
/**********************
|
|
** linx/rtnetlink.h **
|
|
**********************/
|
|
|
|
struct netdev_queue *dev_ingress_queue(struct net_device *dev)
|
|
{
|
|
return dev->ingress_queue;
|
|
}
|
|
|
|
|
|
/****************
|
|
** linux/ip.h **
|
|
****************/
|
|
|
|
struct iphdr *ip_hdr(const struct sk_buff *skb)
|
|
{
|
|
return (struct iphdr *)skb_network_header(skb);
|
|
}
|
|
|
|
|
|
/*******************************
|
|
** asm-generic/bitops/find.h **
|
|
*******************************/
|
|
|
|
unsigned long find_first_zero_bit(unsigned long const *addr, unsigned long size)
|
|
{
|
|
unsigned long i, j;
|
|
|
|
for (i = 0; i < (size / BITS_PER_LONG); i++)
|
|
if (addr[i] != ~0UL)
|
|
break;
|
|
|
|
if (i == size)
|
|
return size;
|
|
|
|
for (j = 0; j < BITS_PER_LONG; j++)
|
|
if ((~addr[i]) & (1 << j))
|
|
break;
|
|
|
|
return (i * BITS_PER_LONG) + j;
|
|
}
|
|
|
|
|
|
/****************************
|
|
** asm-generic/getorder.h **
|
|
****************************/
|
|
|
|
int get_order(unsigned long size)
|
|
{
|
|
int order;
|
|
|
|
size--;
|
|
size >>= PAGE_SHIFT;
|
|
|
|
order = __builtin_ctzl(size);
|
|
return order;
|
|
}
|
|
|
|
|
|
/*********************
|
|
** linux/jiffies.h **
|
|
*********************/
|
|
|
|
clock_t jiffies_to_clock_t(unsigned long j)
|
|
{
|
|
return j / HZ; /* XXX not sure if this is enough */
|
|
}
|
|
|
|
|
|
/*********************
|
|
** linux/utsname.h **
|
|
*********************/
|
|
|
|
struct uts_name init_uts_ns;
|
|
|
|
struct new_utsname *init_utsname(void) { return &init_uts_ns.name; }
|
|
struct new_utsname *utsname(void) { return init_utsname(); }
|
|
|
|
|
|
/**********************
|
|
** linux/notifier.h **
|
|
**********************/
|
|
|
|
int raw_notifier_chain_register(struct raw_notifier_head *nh,
|
|
struct notifier_block *n)
|
|
{
|
|
struct notifier_block *nl = nh->head;
|
|
struct notifier_block *pr = 0;
|
|
while (nl) {
|
|
if (n->priority > nl->priority)
|
|
break;
|
|
pr = nl;
|
|
nl = nl->next;
|
|
}
|
|
|
|
n->next = nl;
|
|
if (pr)
|
|
pr->next = n;
|
|
else
|
|
nh->head = n;
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
int raw_notifier_call_chain(struct raw_notifier_head *nh,
|
|
unsigned long val, void *v)
|
|
{
|
|
int ret = NOTIFY_DONE;
|
|
struct notifier_block *nb = nh->head;
|
|
|
|
while (nb) {
|
|
|
|
ret = nb->notifier_call(nb, val, v);
|
|
if ((ret & NOTIFY_STOP_MASK) == NOTIFY_STOP_MASK)
|
|
break;
|
|
|
|
nb = nb->next;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
|
|
int blocking_notifier_chain_register(struct blocking_notifier_head *nh,
|
|
struct notifier_block *n)
|
|
{
|
|
return raw_notifier_chain_register((struct raw_notifier_head *)nh, n);
|
|
}
|
|
|
|
|
|
int blocking_notifier_call_chain(struct blocking_notifier_head *nh,
|
|
unsigned long val, void *v)
|
|
{
|
|
return raw_notifier_call_chain((struct raw_notifier_head *)nh, val, v);
|
|
}
|
|
|
|
|
|
/****************************
|
|
** asm-generic/checksum.h **
|
|
****************************/
|
|
|
|
__sum16 csum_fold(__wsum csum)
|
|
{
|
|
u32 sum = (u32)csum;
|
|
sum = (sum & 0xffff) + (sum >> 16);
|
|
sum = (sum & 0xffff) + (sum >> 16);
|
|
return (__sum16)~sum;
|
|
}
|
|
|
|
|
|
/*******************
|
|
** net/checksum.h **
|
|
********************/
|
|
|
|
__wsum csum_add(__wsum csum, __wsum addend)
|
|
{
|
|
u32 res = (u32)csum;
|
|
res += (u32)addend;
|
|
return (__wsum)(res + (res < (u32)addend));
|
|
}
|
|
|
|
|
|
__wsum csum_block_add(__wsum csum, __wsum csum2, int offset)
|
|
{
|
|
u32 sum = (u32)csum2;
|
|
if (offset&1)
|
|
sum = ((sum&0xFF00FF)<<8)+((sum>>8)&0xFF00FF);
|
|
return csum_add(csum, (__wsum)sum);
|
|
}
|
|
|
|
|
|
__wsum csum_block_add_ext(__wsum csum, __wsum csum2, int offset, int len)
|
|
{
|
|
return csum_block_add(csum, csum2, offset);
|
|
}
|
|
|
|
|
|
/***************************
|
|
** Linux socket function **
|
|
***************************/
|
|
|
|
static const struct net_proto_family *net_families[NPROTO];
|
|
|
|
|
|
int sock_register(const struct net_proto_family *ops)
|
|
{
|
|
if (ops->family >= NPROTO) {
|
|
printk("protocol %d >= NPROTO (%d)\n", ops->family, NPROTO);
|
|
return -ENOBUFS;
|
|
}
|
|
|
|
net_families[ops->family] = ops;
|
|
pr_info("NET: Registered protocol family %d\n", ops->family);
|
|
return 0;
|
|
}
|
|
|
|
|
|
struct socket *sock_alloc(void)
|
|
{
|
|
struct socket *sock = (struct socket *)kzalloc(sizeof(struct socket), 0);
|
|
|
|
/*
|
|
* Linux normally allocates the socket_wq when calling
|
|
* sock_alloc_inode() while we do it here hoping for the best.
|
|
*/
|
|
sock->wq = (struct socket_wq*)kzalloc(sizeof(*sock->wq), 0);
|
|
if (!sock->wq) {
|
|
kfree(sock);
|
|
return NULL;
|
|
}
|
|
|
|
return sock;
|
|
}
|
|
|
|
|
|
int sock_create_lite(int family, int type, int protocol, struct socket **res)
|
|
|
|
{
|
|
struct socket *sock = sock_alloc();
|
|
|
|
if (!sock)
|
|
return -ENOMEM;
|
|
|
|
sock->type = type;
|
|
*res = sock;
|
|
return 0;
|
|
}
|
|
|
|
|
|
int sock_create_kern(struct net *net, int family, int type, int proto,
|
|
struct socket **res)
|
|
{
|
|
struct socket *sock;
|
|
const struct net_proto_family *pf;
|
|
int err;
|
|
|
|
if (family < 0 || family > NPROTO)
|
|
return -EAFNOSUPPORT;
|
|
|
|
if (type < 0 || type > SOCK_MAX)
|
|
return -EINVAL;
|
|
|
|
pf = net_families[family];
|
|
|
|
if (!pf) {
|
|
printk("No protocol found for family %d\n", family);
|
|
return -ENOPROTOOPT;
|
|
}
|
|
|
|
sock = sock_alloc();
|
|
if (!sock) {
|
|
printk("Could not allocate socket\n");
|
|
return -ENFILE;
|
|
}
|
|
|
|
sock->type = type;
|
|
|
|
err = pf->create(&init_net, sock, proto, 1);
|
|
if (err) {
|
|
kfree(sock);
|
|
return err;
|
|
}
|
|
|
|
*res = sock;
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
static void sock_init(void)
|
|
{
|
|
skb_init();
|
|
}
|
|
|
|
|
|
core_initcall(sock_init);
|
|
|
|
|
|
/*************************
|
|
** Lxip initialization **
|
|
*************************/
|
|
|
|
/*
|
|
* Header declarations and tuning
|
|
*/
|
|
struct net init_net;
|
|
|
|
unsigned long *sysctl_local_reserved_ports;
|
|
struct pernet_operations loopback_net_ops;
|
|
|
|
|
|
/**
|
|
* nr_free_buffer_pages - count number of pages beyond high watermark
|
|
*
|
|
* nr_free_buffer_pages() counts the number of pages which are beyond the
|
|
* high
|
|
* watermark within ZONE_DMA and ZONE_NORMAL.
|
|
*/
|
|
unsigned long nr_free_buffer_pages(void)
|
|
{
|
|
return 1000;
|
|
}
|
|
|
|
|
|
/*
|
|
* Declare stuff used
|
|
*/
|
|
int __ip_auto_config_setup(char *addrs);
|
|
void core_sock_init(void);
|
|
void core_netlink_proto_init(void);
|
|
void subsys_net_dev_init(void);
|
|
void fs_inet_init(void);
|
|
void module_driver_init(void);
|
|
void module_cubictcp_register(void);
|
|
void late_ip_auto_config(void);
|
|
void late_tcp_congestion_default(void);
|
|
|
|
|
|
/**
|
|
* Initialize sub-systems
|
|
*/
|
|
int lxip_init(char const *address_config)
|
|
{
|
|
/* init data */
|
|
INIT_LIST_HEAD(&init_net.dev_base_head);
|
|
|
|
/* call __setup stuff */
|
|
__ip_auto_config_setup((char *)address_config);
|
|
|
|
core_sock_init();
|
|
core_netlink_proto_init();
|
|
|
|
/* sub-systems */
|
|
subsys_net_dev_init();
|
|
fs_inet_init();
|
|
|
|
/* enable local accepts */
|
|
IPV4_DEVCONF_ALL(&init_net, ACCEPT_LOCAL) = 0x1;
|
|
|
|
/* congestion control */
|
|
module_cubictcp_register();
|
|
|
|
/* driver */
|
|
module_driver_init();
|
|
|
|
/* late */
|
|
late_tcp_congestion_default();
|
|
|
|
/* dhcp or static configuration */
|
|
late_ip_auto_config();
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
/******************
|
|
** Lxip private **
|
|
******************/
|
|
|
|
void set_sock_wait(struct socket *sock, unsigned long ptr)
|
|
{
|
|
sock->sk->sk_wq = (struct socket_wq *)ptr;
|
|
}
|
|
|
|
|
|
int socket_check_state(struct socket *socket)
|
|
{
|
|
if (socket->sk->sk_state == TCP_CLOSE_WAIT)
|
|
return -EINTR;
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
void log_sock(struct socket *socket)
|
|
{
|
|
printk("\nNEW socket %p sk %p fsk %x &sk %p &fsk %p\n\n",
|
|
socket, socket->sk, socket->flags, &socket->sk, &socket->flags);
|
|
}
|