#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)
{
write_unlock_bh(&raw_v4_lock);
}
+
+/*
+ * 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,
unsigned long raddr, unsigned long laddr,
int dif)
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) &&
- !(inet->rcv_saddr && inet->rcv_saddr != laddr) &&
+ 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;
}
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;
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;
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;
err = NF_HOOK(PF_INET, NF_IP_LOCAL_OUT, skb, NULL, rt->u.dst.dev,
dst_output);
err = -EFAULT;
kfree_skb(skb);
error:
- IP_INC_STATS(IpOutDiscards);
+ 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;
+ 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;
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;
}
.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);
+
+ if (err)
+ goto done;
+ }
err = ip_route_output_flow(&rt, &fl, sk, !(msg->msg_flags&MSG_DONTWAIT));
}
if (err)
*/
ip_ra_control(sk, 0, NULL);
- inet_sock_release(sk);
+ sk_common_release(sk);
}
/* 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: return err ? err : 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;
}
-static int raw_seticmpfilter(struct sock *sk, char *optval, int optlen)
+static int raw_seticmpfilter(struct sock *sk, char __user *optval, int optlen)
{
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;
}
-static int raw_geticmpfilter(struct sock *sk, char *optval, int *optlen)
+static int raw_geticmpfilter(struct sock *sk, char __user *optval, int __user *optlen)
{
int len, ret = -EFAULT;
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;
}
static int raw_setsockopt(struct sock *sk, int level, int optname,
- char *optval, int optlen)
+ char __user *optval, int optlen)
{
if (level != SOL_RAW)
return ip_setsockopt(sk, level, optname, optval, optlen);
}
static int raw_getsockopt(struct sock *sk, int level, int optname,
- char *optval, int *optlen)
+ char __user *optval, int __user *optlen)
{
if (level != SOL_RAW)
return ip_getsockopt(sk, level, optname, optval, optlen);
switch (cmd) {
case SIOCOUTQ: {
int amount = atomic_read(&sk->sk_wmem_alloc);
- return put_user(amount, (int *)arg);
+ return put_user(amount, (int __user *)arg);
}
case SIOCINQ: {
struct sk_buff *skb;
if (skb != NULL)
amount = skb->len;
spin_unlock_irq(&sk->sk_receive_queue.lock);
- return put_user(amount, (int *)arg);
+ return put_user(amount, (int __user *)arg);
}
default:
#ifdef CONFIG_IP_MROUTE
- return ipmr_ioctl(sk, cmd, arg);
+ return ipmr_ioctl(sk, cmd, (void __user *)arg);
#else
return -ENOIOCTLCMD;
#endif
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,
+ .slab_obj_size = sizeof(struct raw_sock),
};
#ifdef CONFIG_PROC_FS
struct hlist_node *node;
sk_for_each(sk, node, &raw_v4_htable[state->bucket])
- if (sk->sk_family == PF_INET)
+ if (sk->sk_family == PF_INET &&
+ vx_check(sk->sk_xid, VX_IDENT|VX_WATCH))
goto found;
}
sk = NULL;
sk = sk_next(sk);
try_again:
;
- } while (sk && sk->sk_family != PF_INET);
+ } while (sk && (sk->sk_family != PF_INET ||
+ !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,