/*
 * 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.
 *
 *		RAW - implementation of IP "raw" sockets.
 *
 * Version:	@(#)raw.c	1.0.4	05/25/93
 *
 * Authors:	Ross Biro, <bir7@leland.Stanford.Edu>
 *		Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
 *
 * Fixes:
 *		Alan Cox	:	verify_area() fixed up
 *		Alan Cox	:	ICMP error handling
 *		Alan Cox	:	EMSGSIZE if you send too big a packet
 *		Alan Cox	: 	Now uses generic datagrams and shared skbuff
 *					library. No more peek crashes, no more backlogs
 *		Alan Cox	:	Checks sk->broadcast.
 *		Alan Cox	:	Uses skb_free_datagram/skb_copy_datagram
 *		Alan Cox	:	Raw passes ip options too
 *		Alan Cox	:	Setsocketopt added
 *		Alan Cox	:	Fixed error return for broadcasts
 *		Alan Cox	:	Removed wake_up calls
 *		Alan Cox	:	Use ttl/tos
 *		Alan Cox	:	Cleaned up old debugging
 *		Alan Cox	:	Use new kernel side addresses
 *		Alan Cox	:	Use new buffers
 *		Alan Cox	:	Fixed MSG_DONTROUTE
 *		Alan Cox	:	Use new socket send allocator.
 *
 *		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 <asm/system.h>
#include <asm/segment.h>
#include <linux/types.h>
#include <linux/sched.h>
#include <linux/errno.h>
#include <linux/timer.h>
#include <linux/mm.h>
#include <linux/kernel.h>
#include <linux/fcntl.h>
#include <linux/socket.h>
#include <linux/in.h>
#include <linux/inet.h>
#include <linux/netdevice.h>
#include "ip.h"
#include "protocol.h"
#include <linux/skbuff.h>
#include "sock.h"
#include "icmp.h"
#include "udp.h"


struct protocol proto_raw;

static inline unsigned long min(unsigned long a, unsigned long b)
{
	if (a < b) 
		return(a);
	return(b);
}


/* raw_err gets called by the icmp module. */

void raw_err (int type, int code, unsigned char *header, unsigned long daddr,
	 unsigned long saddr, struct inet_protocol *protocol)
{
	struct sock *sk;
   
	if (protocol == NULL) 
		return;
	sk = (struct sock *) protocol->data;
	if (sk == NULL) 
		return;

	/* This is meaningless in raw sockets. */
	if (type == ICMP_SOURCE_QUENCH) 
	{
		if (sk->cong_window > 1) 
			sk->cong_window = sk->cong_window/2;
		return;
	}
	if (type == ICMP_TIME_EXCEEDED)
		return;
		
	sk->err = icmp_err_convert[code & 0xff].errno;
	sk->error_report(sk);
	return;
}


/*
 *	This should be the easiest of all, all we do is
 *	copy it into a buffer.
 */

int raw_input(struct protocol *p, struct protocol *below, sk_buff *skb, void *saddr, void *daddr)
{
	struct sock *sk;

	if (skb == NULL)
		return(0);
  
	sk = (struct sock *) p->user;
	if (sk == NULL) 
	{
		kfree_skb(skb, FREE_READ);
		return(0);
	}

	/* Now we need to copy this into memory. */

	/* Bit of a hack.. fix me later */
	skb_push(skb,skb->ip_hdr->ihl*sizeof(long));
	skb->saddr = *(long *)daddr;
	skb->daddr = *(long *)saddr;

	/* Charge it to the socket. */
	
	if (sock_queue_rcv_skb(sk,skb)<0) 
	{
		ip_statistics.IpInDiscards++;
		kfree_skb(skb, FREE_READ);
		return(0);
	}
	ip_statistics.IpInDelivers++;
	release_sock(sk);
	return(0);
}

/*
 *	Send a RAW IP packet.
 */

static int raw_sendmsg(struct sock *sk, struct msghdr *msg, int len, int noblock, unsigned flags)
{
	sk_buff *skb;
	struct sockaddr_in sin;
	int err;
	struct protocol *p=(struct protocol *)sk->pair;
	
	/*
	 *	Check the flags. Only MSG_DONTROUTE is permitted.
	 */
	 
	if (flags&~MSG_DONTROUTE)
		return(-EINVAL);
	/*
	 *	Get and verify the address. 
	 */

	if (msg->msg_name) 
	{
		memcpy(&sin, msg->msg_name, sizeof(sin));
	}
	else 
	{
		if (sk->state != TCP_ESTABLISHED) 
			return(-EINVAL);
		sin.sin_family = AF_INET;
		sin.sin_port = sk->protocol;
		sin.sin_addr.s_addr = sk->daddr;
	}
	if (sin.sin_port == 0) 
		sin.sin_port = sk->protocol;
  
	sk->inuse = 1;
	if((skb = sock_alloc_send_skb(sk, len + protocol_size(p), noblock, &err))==NULL)
	{
		release_sock(sk);
		return err;
	}
	protocol_adjust(skb, p);

	memcpy_fromiovec(skb_put(skb,len), msg->msg_iov, len);
	
	skb->sk = sk;
	skb->free = 1;
	skb->localroute = sk->localroute | (flags&MSG_DONTROUTE);
	skb->dev = NULL;
	proto_ip.output(&proto_ip, skb, ETH_P_IP, sk->protocol, &sk->saddr, &sin.sin_addr.s_addr, &sk->opt.ip);
	release_sock(sk);
	return(len);
}

static void raw_close(struct sock *sk, int timeout)
{
	sk->inuse = 1;
	sk->state = TCP_CLOSE;
	return protocol_unbind(&proto_ip, (struct protocol *)sk->pair, ETH_P_IP, sk->protocol);
	sk->pair=NULL;
	release_sock(sk);
}


static int raw_initsock(struct sock *sk)
{
	struct protocol *p=protocol_clone(&proto_raw);
	if(p==NULL)
		return -ENOMEM;
	p->user=(void *)sk;
	/* FIXME: ICMP ?? */
	/* We need to remember this somewhere. */
	sk->pair = (struct sock *)p;
	return protocol_bind(&proto_ip, p, ETH_P_IP, sk->protocol);
}


/*
 *	This should be easy, if there is something there
 *	we return it, otherwise we block.
 */

int raw_recvmsg(struct sock *sk, struct msghdr *msg, int len,
		int noblock, unsigned flags,int *addr_len)
{
	int copied=0;
	sk_buff *skb;
	int err;
	int truesize;
	struct sockaddr_in *sin=(struct sockaddr_in *)msg->msg_name;
	if (sk->shutdown & RCV_SHUTDOWN) 
		return(0);

	if (addr_len) 
		*addr_len=sizeof(*sin);

	skb=skb_recv_datagram(sk,flags,noblock,&err);
	if(skb==NULL)
 		return err;

	truesize=skb->len;
	copied = min(len, truesize);
  
	skb_copy_datagram_iovec(skb, 0, msg->msg_iov, copied);
	sk->stamp=skb->stamp;

	/* Copy the address. */
	if (sin) 
	{
		sin->sin_family = AF_INET;
		sin->sin_addr.s_addr = skb->daddr;
	}
	skb_free_datagram(skb);
	release_sock(sk);
	return (truesize);	/* len not copied. BSD returns the true size of the message so you know a bit fell off! */
}

struct proto raw_prot = {
	raw_close,		/* Close a RAW socket 		*/
	raw_sendmsg,		/* Send a RAW message 		*/
	raw_recvmsg,		/* Receive a RAW message	*/
	udp_connect,		/* Connect a raw socket		*/
	NULL,			/* No accept() call		*/
	NULL,			/* No retransmit function	*/
	NULL,			/* No write wakeup		*/
	NULL,			/* No read wakeup		*/
	NULL/*raw_rcv*/,	/* Obsolete 			*/
	datagram_select,	/* Select using generic		*/
	NULL,			/* No ioctl calls		*/
	raw_initsock,		/* Initialise a raw socket	*/
	NULL,			/* No shutdown function		*/
	ip_setsockopt,		/* Set options on raw IP sockets*/
	ip_getsockopt,		/* Get options on socket	*/
	128,			/* Max header size		*/
	0,			/* Retransmit counter		*/
	{NULL,},		/* Socket array			*/
	"RAW"			/* Name				*/
};

static int raw_get_key(int protocol, int subid, unsigned char *key)
{
	return -EAFNOSUPPORT;
}

struct protocol proto_raw=
{
	NULL,
	"RAW",
	sizeof(struct iphdr),
	0,
	sizeof(struct iphdr),
	0,
	NULL,
	raw_input,
	raw_input,
	default_protocol_control,
	raw_get_key,
	NULL,
	NULL
};


void raw_init(void)
{
	protocol_register(&proto_raw);
}
