*
* Version: $Id: raw.c,v 1.64 2002/02/01 22:01:04 davem Exp $
*
- * Authors: Ross Biro, <bir7@leland.Stanford.Edu>
+ * Authors: Ross Biro
* Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
*
* Fixes:
*/
#include <linux/config.h>
+#include <linux/types.h>
#include <asm/atomic.h>
#include <asm/byteorder.h>
#include <asm/current.h>
#include <asm/uaccess.h>
#include <asm/ioctls.h>
-#include <linux/types.h>
#include <linux/stddef.h>
#include <linux/slab.h>
#include <linux/errno.h>
#include <linux/netdevice.h>
#include <linux/in_route.h>
#include <linux/route.h>
-#include <linux/tcp.h>
#include <linux/skbuff.h>
#include <net/dst.h>
#include <net/sock.h>
#include <net/udp.h>
#include <net/raw.h>
#include <net/snmp.h>
+#include <net/tcp_states.h>
#include <net/inet_common.h>
#include <net/checksum.h>
#include <net/xfrm.h>
#include <linux/netfilter_ipv4.h>
struct hlist_head raw_v4_htable[RAWV4_HTABLE_SIZE];
-rwlock_t raw_v4_lock = RW_LOCK_UNLOCKED;
+DEFINE_RWLOCK(raw_v4_lock);
static void raw_v4_hash(struct sock *sk)
{
/*
- Check if an address is in the list
-*/
-static inline int raw_addr_in_list (
- u32 rcv_saddr1,
- u32 rcv_saddr2,
- u32 loc_addr,
- struct nx_info *nx_info)
-{
- int ret = 0;
- if (loc_addr != 0 &&
- (rcv_saddr1 == loc_addr || rcv_saddr2 == loc_addr))
- ret = 1;
- else if (rcv_saddr1 == 0) {
- /* Accept any address or only the one in the list */
- if (nx_info == NULL)
- ret = 1;
- else {
- int n = nx_info->nbipv4;
- int i;
- for (i=0; i<n; i++) {
- if (nx_info->ipv4[i] == loc_addr) {
- ret = 1;
- break;
- }
- }
- }
- }
- return ret;
+ * Check if a given address matches for a socket
+ *
+ * nxi: the socket's nx_info if any
+ * addr: to be verified address
+ * saddr/baddr: socket addresses
+ */
+static inline int raw_addr_match (
+ struct nx_info *nxi,
+ uint32_t addr,
+ uint32_t saddr,
+ uint32_t baddr)
+{
+ if (addr && (saddr == addr || baddr == addr))
+ return 1;
+ if (!saddr)
+ return addr_in_nx_info(nxi, addr);
+ return 0;
}
struct sock *__raw_v4_lookup(struct sock *sk, unsigned short num,
struct hlist_node *node;
sk_for_each_from(sk, node) {
- struct inet_opt *inet = inet_sk(sk);
+ struct inet_sock *inet = inet_sk(sk);
if (inet->num == num &&
!(inet->daddr && inet->daddr != raddr) &&
- raw_addr_in_list(inet->rcv_saddr, inet->rcv_saddr2,
- laddr, sk->sk_nx_info) &&
+ raw_addr_match(sk->sk_nx_info, laddr,
+ inet->rcv_saddr, inet->rcv_saddr2) &&
!(sk->sk_bound_dev_if && sk->sk_bound_dev_if != dif))
goto found; /* gotcha */
}
{
int type;
+ if (!pskb_may_pull(skb, sizeof(struct icmphdr)))
+ return 1;
+
type = skb->h.icmph->type;
if (type < 32) {
- __u32 data = raw4_sk(sk)->filter.data;
+ __u32 data = raw_sk(sk)->filter.data;
return ((1 << type) & data) != 0;
}
* RFC 1122: SHOULD pass TOS value up to the transport layer.
* -> It does. And not only TOS, but all IP header.
*/
-void raw_v4_input(struct sk_buff *skb, struct iphdr *iph, int hash)
+int raw_v4_input(struct sk_buff *skb, struct iphdr *iph, int hash)
{
struct sock *sk;
struct hlist_head *head;
+ int delivered = 0;
read_lock(&raw_v4_lock);
head = &raw_v4_htable[hash];
skb->dev->ifindex);
while (sk) {
+ delivered = 1;
if (iph->protocol != IPPROTO_ICMP || !icmp_filter(sk, skb)) {
struct sk_buff *clone = skb_clone(skb, GFP_ATOMIC);
}
out:
read_unlock(&raw_v4_lock);
+ return delivered;
}
void raw_err (struct sock *sk, struct sk_buff *skb, u32 info)
{
- struct inet_opt *inet = inet_sk(sk);
+ struct inet_sock *inet = inet_sk(sk);
int type = skb->h.icmph->type;
int code = skb->h.icmph->code;
int err = 0;
kfree_skb(skb);
return NET_RX_DROP;
}
+ nf_reset(skb);
skb_push(skb, skb->data - skb->nh.raw);
return 0;
}
-static int raw_send_hdrinc(struct sock *sk, void *from, int length,
+static int raw_send_hdrinc(struct sock *sk, void *from, size_t length,
struct rtable *rt,
unsigned int flags)
{
- struct inet_opt *inet = inet_sk(sk);
+ struct inet_sock *inet = inet_sk(sk);
int hh_len;
struct iphdr *iph;
struct sk_buff *skb;
goto error_fault;
/* We don't modify invalid header */
- if (length >= sizeof(*iph) && iph->ihl * 4 <= length) {
+ if (length >= sizeof(*iph) && iph->ihl * 4U <= length) {
if (!iph->saddr)
iph->saddr = rt->rt_src;
iph->check = 0;
iph->check = ip_fast_csum((unsigned char *)iph, iph->ihl);
}
+ err = -EPERM;
+ if (!vx_check(0, VX_ADMIN) && !capable(CAP_NET_RAW)
+ && (!addr_in_nx_info(sk->sk_nx_info, iph->saddr)))
+ goto error_free;
+
err = NF_HOOK(PF_INET, NF_IP_LOCAL_OUT, skb, NULL, rt->u.dst.dev,
dst_output);
if (err > 0)
error_fault:
err = -EFAULT;
+error_free:
kfree_skb(skb);
error:
- IP_INC_STATS(OutDiscards);
+ IP_INC_STATS(IPSTATS_MIB_OUTDISCARDS);
return err;
}
+static void raw_probe_proto_opt(struct flowi *fl, struct msghdr *msg)
+{
+ struct iovec *iov;
+ u8 __user *type = NULL;
+ u8 __user *code = NULL;
+ int probed = 0;
+ unsigned int i;
+
+ if (!msg->msg_iov)
+ return;
+
+ for (i = 0; i < msg->msg_iovlen; i++) {
+ iov = &msg->msg_iov[i];
+ if (!iov)
+ continue;
+
+ switch (fl->proto) {
+ case IPPROTO_ICMP:
+ /* check if one-byte field is readable or not. */
+ if (iov->iov_base && iov->iov_len < 1)
+ break;
+
+ if (!type) {
+ type = iov->iov_base;
+ /* check if code field is readable or not. */
+ if (iov->iov_len > 1)
+ code = type + 1;
+ } else if (!code)
+ code = iov->iov_base;
+
+ if (type && code) {
+ get_user(fl->fl_icmp_type, type);
+ get_user(fl->fl_icmp_code, code);
+ probed = 1;
+ }
+ break;
+ default:
+ probed = 1;
+ break;
+ }
+ if (probed)
+ break;
+ }
+}
+
static int raw_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg,
size_t len)
{
- struct inet_opt *inet = inet_sk(sk);
+ struct inet_sock *inet = inet_sk(sk);
struct ipcm_cookie ipc;
struct rtable *rt = NULL;
int free = 0;
int err;
err = -EMSGSIZE;
- if (len < 0 || len > 0xFFFF)
+ if (len > 0xFFFF)
goto out;
/*
printk(KERN_INFO "%s forgot to set AF_INET in "
"raw sendmsg. Fix it!\n",
current->comm);
- err = -EINVAL;
+ err = -EAFNOSUPPORT;
if (usin->sin_family)
goto out;
}
daddr = ipc.opt->faddr;
}
}
- tos = RT_TOS(inet->tos) | sk->sk_localroute;
+ tos = RT_CONN_FLAGS(sk);
if (msg->msg_flags & MSG_DONTROUTE)
tos |= RTO_ONLINK;
.proto = inet->hdrincl ? IPPROTO_RAW :
sk->sk_protocol,
};
-
+ if (!inet->hdrincl)
+ raw_probe_proto_opt(&fl, msg);
+
if (sk->sk_nx_info) {
err = ip_find_src(sk->sk_nx_info, &rt, &fl);
kfree(ipc.opt);
ip_rt_put(rt);
-out: return err < 0 ? err : len;
+out:
+ if (err < 0)
+ return err;
+ return len;
do_confirm:
dst_confirm(&rt->u.dst);
/* This gets rid of all the nasties in af_inet. -DaveM */
static int raw_bind(struct sock *sk, struct sockaddr *uaddr, int addr_len)
{
- struct inet_opt *inet = inet_sk(sk);
+ struct inet_sock *inet = inet_sk(sk);
struct sockaddr_in *addr = (struct sockaddr_in *) uaddr;
int ret = -EINVAL;
int chk_addr_ret;
* we return it, otherwise we block.
*/
-int raw_recvmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg,
- size_t len, int noblock, int flags, int *addr_len)
+static int raw_recvmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg,
+ size_t len, int noblock, int flags, int *addr_len)
{
- struct inet_opt *inet = inet_sk(sk);
+ struct inet_sock *inet = inet_sk(sk);
size_t copied = 0;
int err = -EOPNOTSUPP;
struct sockaddr_in *sin = (struct sockaddr_in *)msg->msg_name;
}
if (inet->cmsg_flags)
ip_cmsg_recv(msg, skb);
+ if (flags & MSG_TRUNC)
+ copied = skb->len;
done:
skb_free_datagram(sk, skb);
-out: return err ? : copied;
+out:
+ if (err)
+ return err;
+ return copied;
}
static int raw_init(struct sock *sk)
{
- struct raw_opt *tp = raw4_sk(sk);
+ struct raw_sock *rp = raw_sk(sk);
+
if (inet_sk(sk)->num == IPPROTO_ICMP)
- memset(&tp->filter, 0, sizeof(tp->filter));
+ memset(&rp->filter, 0, sizeof(rp->filter));
return 0;
}
{
if (optlen > sizeof(struct icmp_filter))
optlen = sizeof(struct icmp_filter);
- if (copy_from_user(&raw4_sk(sk)->filter, optval, optlen))
+ if (copy_from_user(&raw_sk(sk)->filter, optval, optlen))
return -EFAULT;
return 0;
}
len = sizeof(struct icmp_filter);
ret = -EFAULT;
if (put_user(len, optlen) ||
- copy_to_user(optval, &raw4_sk(sk)->filter, len))
+ copy_to_user(optval, &raw_sk(sk)->filter, len))
goto out;
ret = 0;
out: return ret;
struct sk_buff *skb;
int amount = 0;
- spin_lock_irq(&sk->sk_receive_queue.lock);
+ spin_lock_bh(&sk->sk_receive_queue.lock);
skb = skb_peek(&sk->sk_receive_queue);
if (skb != NULL)
amount = skb->len;
- spin_unlock_irq(&sk->sk_receive_queue.lock);
+ spin_unlock_bh(&sk->sk_receive_queue.lock);
return put_user(amount, (int __user *)arg);
}
struct proto raw_prot = {
.name = "RAW",
+ .owner = THIS_MODULE,
.close = raw_close,
- .connect = udp_connect,
+ .connect = ip4_datagram_connect,
.disconnect = udp_disconnect,
.ioctl = raw_ioctl,
.init = raw_init,
.backlog_rcv = raw_rcv_skb,
.hash = raw_v4_hash,
.unhash = raw_v4_unhash,
+ .obj_size = sizeof(struct raw_sock),
};
#ifdef CONFIG_PROC_FS
sk_for_each(sk, node, &raw_v4_htable[state->bucket])
if (sk->sk_family == PF_INET &&
- vx_check(sk->sk_xid, VX_WATCH|VX_IDENT))
+ vx_check(sk->sk_xid, VX_IDENT|VX_WATCH))
goto found;
}
sk = NULL;
try_again:
;
} while (sk && (sk->sk_family != PF_INET ||
- !vx_check(sk->sk_xid, VX_WATCH|VX_IDENT)));
+ !vx_check(sk->sk_xid, VX_IDENT|VX_WATCH)));
if (!sk && ++state->bucket < RAWV4_HTABLE_SIZE) {
sk = sk_head(&raw_v4_htable[state->bucket]);
static __inline__ char *get_raw_sock(struct sock *sp, char *tmpbuf, int i)
{
- struct inet_opt *inet = inet_sk(sp);
+ struct inet_sock *inet = inet_sk(sp);
unsigned int dest = inet->daddr,
src = inet->rcv_saddr;
__u16 destp = 0,