X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;f=net%2Fipv4%2Fraw.c;h=dfb9e7b3fe83ce5b2c6e14f9c39cb4e6e8055f8e;hb=6a77f38946aaee1cd85eeec6cf4229b204c15071;hp=655aeba924d2f4c71d638af03fae01bfe067b159;hpb=9213980e6a70d8473e0ffd4b39ab5b6caaba9ff5;p=linux-2.6.git diff --git a/net/ipv4/raw.c b/net/ipv4/raw.c index 655aeba92..dfb9e7b3f 100644 --- a/net/ipv4/raw.c +++ b/net/ipv4/raw.c @@ -81,7 +81,7 @@ #include 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) { @@ -104,34 +104,23 @@ static void raw_v4_unhash(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; iipv4[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, @@ -141,12 +130,12 @@ 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 */ } @@ -163,9 +152,12 @@ static __inline__ int icmp_filter(struct sock *sk, struct sk_buff *skb) { 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; } @@ -211,7 +203,7 @@ out: 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; @@ -293,7 +285,7 @@ static int raw_send_hdrinc(struct sock *sk, void *from, int 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; @@ -338,6 +330,10 @@ static int raw_send_hdrinc(struct sock *sk, void *from, int length, 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); @@ -352,14 +348,59 @@ error_fault: err = -EFAULT; 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; + 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; @@ -395,7 +436,7 @@ static int raw_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg, 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; } @@ -462,7 +503,9 @@ static int raw_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg, .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); @@ -520,13 +563,13 @@ static void raw_close(struct sock *sk, long timeout) */ 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; @@ -551,10 +594,10 @@ out: return 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; @@ -595,16 +638,19 @@ int raw_recvmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg, } 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; } @@ -612,7 +658,7 @@ 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; } @@ -630,7 +676,7 @@ static int raw_geticmpfilter(struct sock *sk, char __user *optval, int __user *o 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; @@ -696,8 +742,9 @@ static int raw_ioctl(struct sock *sk, int cmd, unsigned long 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, @@ -709,6 +756,7 @@ struct proto raw_prot = { .backlog_rcv = raw_rcv_skb, .hash = raw_v4_hash, .unhash = raw_v4_unhash, + .slab_obj_size = sizeof(struct raw_sock), }; #ifdef CONFIG_PROC_FS @@ -728,7 +776,7 @@ static struct sock *raw_get_first(struct seq_file *seq) 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; @@ -745,7 +793,7 @@ static struct sock *raw_get_next(struct seq_file *seq, struct sock *sk) 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]); @@ -789,7 +837,7 @@ static void raw_seq_stop(struct seq_file *seq, void *v) 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,