patch-2.1.120 linux/net/ipv6/udp.c
Next file: linux/net/ipx/af_ipx.c
Previous file: linux/net/ipv6/tcp_ipv6.c
Back to the patch index
Back to the overall index
- Lines: 549
- Date:
Mon Aug 31 10:32:19 1998
- Orig file:
v2.1.119/linux/net/ipv6/udp.c
- Orig date:
Thu Aug 27 19:56:31 1998
diff -u --recursive --new-file v2.1.119/linux/net/ipv6/udp.c linux/net/ipv6/udp.c
@@ -7,7 +7,7 @@
*
* Based on linux/ipv4/udp.c
*
- * $Id: udp.c,v 1.31 1998/07/15 05:05:45 davem Exp $
+ * $Id: udp.c,v 1.33 1998/08/27 16:55:20 davem Exp $
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@@ -15,6 +15,7 @@
* 2 of the License, or (at your option) any later version.
*/
+#include <linux/config.h>
#include <linux/errno.h>
#include <linux/types.h>
#include <linux/socket.h>
@@ -59,6 +60,14 @@
if((sk2->num == snum) && (sk2 != sk)) {
unsigned char state = sk2->state;
int sk2_reuse = sk2->reuse;
+
+ /* Two sockets can be bound to the same port if they're
+ * bound to different interfaces.
+ */
+
+ if(sk2->bound_dev_if != sk->bound_dev_if)
+ continue;
+
if(addr_type == IPV6_ADDR_ANY || (!sk2->rcv_saddr)) {
if((!sk2_reuse) ||
(!sk_reuse) ||
@@ -139,7 +148,7 @@
}
static struct sock *udp_v6_lookup(struct in6_addr *saddr, u16 sport,
- struct in6_addr *daddr, u16 dport)
+ struct in6_addr *daddr, u16 dport, int dif)
{
struct sock *sk, *result = NULL;
unsigned short hnum = ntohs(dport);
@@ -166,7 +175,12 @@
continue;
score++;
}
- if(score == 3) {
+ if(sk->bound_dev_if) {
+ if(sk->bound_dev_if != dif)
+ continue;
+ score++;
+ }
+ if(score == 4) {
result = sk;
break;
} else if(score > badness) {
@@ -257,20 +271,25 @@
*/
fl.proto = IPPROTO_UDP;
- fl.nl_u.ip6_u.daddr = daddr;
+ fl.nl_u.ip6_u.daddr = &np->daddr;
fl.nl_u.ip6_u.saddr = NULL;
fl.oif = sk->bound_dev_if;
fl.uli_u.ports.dport = sk->dport;
fl.uli_u.ports.sport = sk->sport;
+ if (np->opt && np->opt->srcrt) {
+ struct rt0_hdr *rt0 = (struct rt0_hdr *) np->opt->srcrt;
+ fl.nl_u.ip6_u.daddr = rt0->addr;
+ }
+
dst = ip6_route_output(sk, &fl);
-
+
if (dst->error) {
dst_release(dst);
return dst->error;
}
- ip6_dst_store(sk, dst);
+ ip6_dst_store(sk, dst, fl.nl_u.ip6_u.daddr);
/* get the source adddress used in the apropriate device */
@@ -291,15 +310,50 @@
static void udpv6_close(struct sock *sk, unsigned long timeout)
{
- lock_sock(sk);
+ /* See for explanation: raw_close in ipv4/raw.c */
sk->state = TCP_CLOSE;
- ipv6_sock_mc_close(sk);
udp_v6_unhash(sk);
sk->dead = 1;
- release_sock(sk);
destroy_sock(sk);
}
+#ifdef CONFIG_FILTER
+#undef CONFIG_UDP_DELAY_CSUM
+#endif
+
+#ifdef CONFIG_UDP_DELAY_CSUM
+
+/* Please, read comments in net/checksum.h, asm/checksum.h
+
+ I commented out csum_partial_copy_to_user there because it did not
+ verify_area. Now I am even wondered, how clever was I that time 8)8)
+ If I did not it, I would step into this hole again. --ANK
+ */
+
+#ifndef _HAVE_ARCH_COPY_AND_CSUM_TO_USER
+#if defined(__i386__)
+static __inline__
+unsigned int csum_and_copy_to_user (const char *src, char *dst,
+ int len, int sum, int *err_ptr)
+{
+ int *src_err_ptr=NULL;
+
+ if (verify_area(VERIFY_WRITE, dst, len) == 0)
+ return csum_partial_copy_generic(src, dst, len, sum, src_err_ptr, err_ptr);
+
+ if (len)
+ *err_ptr = -EFAULT;
+
+ return sum;
+}
+#elif defined(__sparc__)
+#define csum_and_copy_to_user csum_partial_copy_to_user
+#else
+#undef CONFIG_UDP_DELAY_CSUM
+#endif
+#endif
+#endif
+
/*
* This should be easy, if there is something there we
* return it, otherwise we block.
@@ -322,12 +376,12 @@
* From here the generic datagram does a lot of the work. Come
* the finished NET3, it will do _ALL_ the work!
*/
-
+
skb = skb_recv_datagram(sk, flags, noblock, &err);
if (!skb)
goto out;
- copied = ntohs(((struct udphdr *)skb->h.raw)->len) - sizeof(struct udphdr);
+ copied = skb->len - sizeof(struct udphdr);
if (copied > len) {
copied = len;
msg->msg_flags |= MSG_TRUNC;
@@ -337,8 +391,41 @@
* FIXME : should use udp header size info value
*/
+#ifndef CONFIG_UDP_DELAY_CSUM
err = skb_copy_datagram_iovec(skb, sizeof(struct udphdr),
msg->msg_iov, copied);
+#else
+ if (sk->no_check || skb->ip_summed==CHECKSUM_UNNECESSARY) {
+ err = skb_copy_datagram_iovec(skb, sizeof(struct udphdr), msg->msg_iov,
+ copied);
+ } else if (copied > msg->msg_iov[0].iov_len || (msg->msg_flags&MSG_TRUNC)) {
+ if (csum_fold(csum_partial(skb->h.raw, ntohs(skb->h.uh->len), skb->csum))) {
+ /* Error for blocking case is chosen to masquerade
+ as some normal condition.
+ */
+ err = (msg->msg_flags&MSG_DONTWAIT) ? -EAGAIN : -EHOSTUNREACH;
+ udp_stats_in6.UdpInErrors++;
+ goto out_free;
+ }
+ err = skb_copy_datagram_iovec(skb, sizeof(struct udphdr), msg->msg_iov,
+ copied);
+ } else {
+ unsigned int csum = csum_partial(skb->h.raw, sizeof(struct udphdr), skb->csum);
+
+ err = 0;
+ csum = csum_and_copy_to_user((char*)&skb->h.uh[1], msg->msg_iov[0].iov_base, copied, csum, &err);
+ if (err)
+ goto out_free;
+ if (csum_fold(csum)) {
+ /* Error for blocking case is chosen to masquerade
+ as some normal condition.
+ */
+ err = (msg->msg_flags&MSG_DONTWAIT) ? -EAGAIN : -EHOSTUNREACH;
+ udp_stats_in6.UdpInErrors++;
+ goto out_free;
+ }
+ }
+#endif
if (err)
goto out_free;
@@ -361,7 +448,7 @@
memcpy(&sin6->sin6_addr, &skb->nh.ipv6h->saddr,
sizeof(struct in6_addr));
- if (msg->msg_controllen)
+ if (sk->net_pinfo.af_inet6.rxopt.all)
datagram_recv_ctl(sk, msg, skb);
}
}
@@ -373,20 +460,27 @@
return err;
}
-void udpv6_err(struct sk_buff *skb, int type, int code, unsigned char *buff, __u32 info,
- struct in6_addr *saddr, struct in6_addr *daddr,
- struct inet6_protocol *protocol)
-{
+void udpv6_err(struct sk_buff *skb, struct ipv6hdr *hdr,
+ struct inet6_skb_parm *opt,
+ int type, int code, unsigned char *buff, __u32 info)
+{
+ struct device *dev = skb->dev;
+ struct in6_addr *saddr = &hdr->saddr;
+ struct in6_addr *daddr = &hdr->daddr;
struct sock *sk;
struct udphdr *uh;
int err;
-
+
+ if (buff + sizeof(struct udphdr) > skb->tail)
+ return;
+
uh = (struct udphdr *) buff;
- sk = udp_v6_lookup(daddr, uh->dest, saddr, uh->source);
+ sk = udp_v6_lookup(daddr, uh->dest, saddr, uh->source, dev->ifindex);
if (sk == NULL) {
- printk(KERN_DEBUG "icmp for unknown sock\n");
+ if (net_ratelimit())
+ printk(KERN_DEBUG "icmp for unknown sock\n");
return;
}
@@ -407,11 +501,10 @@
if (sock_queue_rcv_skb(sk,skb)<0) {
udp_stats_in6.UdpInErrors++;
ipv6_statistics.Ip6InDiscards++;
- ipv6_statistics.Ip6InDelivers--;
- skb->sk = NULL;
kfree_skb(skb);
return 0;
}
+ ipv6_statistics.Ip6InDelivers++;
udp_stats_in6.UdpInDatagrams++;
return 0;
}
@@ -430,7 +523,8 @@
static struct sock *udp_v6_mcast_next(struct sock *sk,
u16 loc_port, struct in6_addr *loc_addr,
- u16 rmt_port, struct in6_addr *rmt_addr)
+ u16 rmt_port, struct in6_addr *rmt_addr,
+ int dif)
{
struct sock *s = sk;
unsigned short num = ntohs(loc_port);
@@ -446,6 +540,9 @@
ipv6_addr_cmp(&np->daddr, rmt_addr))
continue;
+ if (s->bound_dev_if && s->bound_dev_if != dif)
+ continue;
+
if(!ipv6_addr_any(&np->rcv_saddr)) {
if(ipv6_addr_cmp(&np->rcv_saddr, loc_addr) == 0)
return s;
@@ -468,16 +565,18 @@
{
struct sock *sk, *sk2;
struct sk_buff *buff;
+ int dif;
sk = udp_hash[ntohs(uh->dest) & (UDP_HTABLE_SIZE - 1)];
- sk = udp_v6_mcast_next(sk, uh->dest, daddr, uh->source, saddr);
+ dif = skb->dev->ifindex;
+ sk = udp_v6_mcast_next(sk, uh->dest, daddr, uh->source, saddr, dif);
if (!sk)
goto free_skb;
buff = NULL;
sk2 = sk;
while((sk2 = udp_v6_mcast_next(sk2->next, uh->dest, saddr,
- uh->source, daddr))) {
+ uh->source, daddr, dif))) {
if (!buff) {
buff = skb_clone(skb, GFP_ATOMIC);
if (!buff)
@@ -486,59 +585,70 @@
if (sock_queue_rcv_skb(sk2, buff) >= 0)
buff = NULL;
}
- if (buff) {
- buff->sk = NULL;
+ if (buff)
kfree_skb(buff);
- }
if (sock_queue_rcv_skb(sk, skb) < 0) {
- free_skb:
- skb->sk = NULL;
+free_skb:
kfree_skb(skb);
}
}
-int udpv6_rcv(struct sk_buff *skb, struct device *dev,
- struct in6_addr *saddr, struct in6_addr *daddr,
- struct ipv6_options *opt, unsigned short len,
- int redo, struct inet6_protocol *protocol)
+int udpv6_rcv(struct sk_buff *skb, unsigned long len)
{
struct sock *sk;
struct udphdr *uh;
- int ulen;
+ struct device *dev = skb->dev;
+ struct in6_addr *saddr = &skb->nh.ipv6h->saddr;
+ struct in6_addr *daddr = &skb->nh.ipv6h->daddr;
+ u32 ulen;
- /*
- * check if the address is ours...
- * I believe that this is being done in IP layer
- */
-
- uh = (struct udphdr *) skb->h.uh;
-
- ipv6_statistics.Ip6InDelivers++;
+ uh = skb->h.uh;
+ __skb_pull(skb, skb->h.raw - skb->data);
ulen = ntohs(uh->len);
-
+
+ /* Check for jumbo payload */
+ if (ulen == 0 && skb->nh.ipv6h->payload_len == 0)
+ ulen = len;
+
if (ulen > len || len < sizeof(*uh)) {
- printk(KERN_DEBUG "UDP: short packet: %d/%d\n", ulen, len);
+ if (net_ratelimit())
+ printk(KERN_DEBUG "UDP: short packet: %d/%ld\n", ulen, len);
udp_stats_in6.UdpInErrors++;
kfree_skb(skb);
return(0);
}
if (uh->check == 0) {
- printk(KERN_DEBUG "IPv6: udp checksum is 0\n");
+ /* IPv6 draft-v2 section 8.1 says that we SHOULD log
+ this error. Well, it is reasonable.
+ */
+ if (net_ratelimit())
+ printk(KERN_INFO "IPv6: udp checksum is 0\n");
goto discard;
}
+ skb_trim(skb, ulen);
+
+#ifndef CONFIG_UDP_DELAY_CSUM
switch (skb->ip_summed) {
case CHECKSUM_NONE:
- skb->csum = csum_partial((char*)uh, len, 0);
+ skb->csum = csum_partial((char*)uh, ulen, 0);
case CHECKSUM_HW:
- if (csum_ipv6_magic(saddr, daddr, len, IPPROTO_UDP, skb->csum)) {
+ if (csum_ipv6_magic(saddr, daddr, ulen, IPPROTO_UDP, skb->csum)) {
printk(KERN_DEBUG "IPv6: udp checksum error\n");
goto discard;
}
};
-
+#else
+ if (skb->ip_summed==CHECKSUM_HW) {
+ if (csum_ipv6_magic(saddr, daddr, ulen, IPPROTO_UDP, skb->csum))
+ goto discard;
+ skb->ip_summed = CHECKSUM_UNNECESSARY;
+ } else if (skb->ip_summed != CHECKSUM_UNNECESSARY)
+ skb->csum = ~csum_ipv6_magic(saddr, daddr, ulen, IPPROTO_UDP, 0);
+#endif
+
len = ulen;
/*
@@ -555,10 +665,16 @@
* check socket cache ... must talk to Alan about his plans
* for sock caches... i'll skip this for now.
*/
-
- sk = udp_v6_lookup(saddr, uh->source, daddr, uh->dest);
-
+
+ sk = udp_v6_lookup(saddr, uh->source, daddr, uh->dest, dev->ifindex);
+
if (sk == NULL) {
+#ifdef CONFIG_UDP_DELAY_CSUM
+ if (skb->ip_summed != CHECKSUM_UNNECESSARY &&
+ csum_fold(csum_partial((char*)uh, len, skb->csum)))
+ goto discard;
+#endif
+
udp_stats_in6.UdpNoPorts++;
icmpv6_send(skb, ICMPV6_DEST_UNREACH, ICMPV6_PORT_UNREACH, 0, dev);
@@ -566,16 +682,13 @@
kfree_skb(skb);
return(0);
}
-
+
/* deliver */
-
- if (atomic_read(&sk->sock_readers))
- __skb_queue_tail(&sk->back_log, skb);
- else
- udpv6_queue_rcv_skb(sk, skb);
+
+ udpv6_queue_rcv_skb(sk, skb);
return(0);
-
+
discard:
udp_stats_in6.UdpInErrors++;
kfree_skb(skb);
@@ -618,7 +731,7 @@
}
if (csum_partial_copy_fromiovecend(dst, udh->iov, offset,
- clen, &udh->wcheck))
+ clen, &udh->wcheck))
return -EFAULT;
if (final) {
@@ -649,11 +762,11 @@
static int udpv6_sendmsg(struct sock *sk, struct msghdr *msg, int ulen)
{
- struct ipv6_options opt_space;
+ struct ipv6_txoptions opt_space;
struct udpv6fakehdr udh;
struct ipv6_pinfo *np = &sk->net_pinfo.af_inet6;
struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *) msg->msg_name;
- struct ipv6_options *opt = NULL;
+ struct ipv6_txoptions *opt = NULL;
struct flowi fl;
int addr_len = msg->msg_namelen;
struct in6_addr *daddr;
@@ -661,22 +774,18 @@
int len = ulen + sizeof(struct udphdr);
int addr_type;
int hlimit = -1;
-
+
int err;
/* Rough check on arithmetic overflow,
better check is made in ip6_build_xmit
-
- When jumbo header will be implemeted we will change it
- to something sort of (len will be size_t)
- ulen > SIZE_T_MAX - sizeof(struct udphdr)
- */
- if (ulen < 0 || ulen > 0xFFFF - sizeof(struct udphdr))
+ */
+ if (ulen < 0 || ulen > INT_MAX - sizeof(struct udphdr))
return -EMSGSIZE;
-
+
if (msg->msg_flags & ~(MSG_DONTROUTE|MSG_DONTWAIT))
return(-EINVAL);
-
+
if (sin6) {
if (sin6->sin6_family == AF_INET)
return udp_sendmsg(sk, msg, ulen);
@@ -692,14 +801,6 @@
udh.uh.dest = sin6->sin6_port;
daddr = &sin6->sin6_addr;
-
- /* BUGGGG! If route is not cloned, this check always
- fails, hence dst_cache only slows down transmission --ANK
- */
- if (sk->dst_cache && ipv6_addr_cmp(daddr, &np->daddr)) {
- dst_release(sk->dst_cache);
- sk->dst_cache = NULL;
- }
} else {
if (sk->state != TCP_ESTABLISHED)
return(-ENOTCONN);
@@ -707,9 +808,9 @@
udh.uh.dest = sk->dport;
daddr = &sk->net_pinfo.af_inet6.daddr;
}
-
+
addr_type = ipv6_addr_type(daddr);
-
+
if (addr_type == IPV6_ADDR_MAPPED) {
struct sockaddr_in sin;
@@ -720,24 +821,25 @@
return udp_sendmsg(sk, msg, ulen);
}
-
+
udh.daddr = NULL;
fl.oif = sk->bound_dev_if;
if (msg->msg_controllen) {
opt = &opt_space;
- memset(opt, 0, sizeof(struct ipv6_options));
+ memset(opt, 0, sizeof(struct ipv6_txoptions));
err = datagram_send_ctl(msg, &fl.oif, &saddr, opt, &hlimit);
if (err < 0)
return err;
-
- if (opt->srcrt)
- udh.daddr = daddr;
}
-
+ if (opt == NULL || !(opt->opt_nflen|opt->opt_flen))
+ opt = np->opt;
+ if (opt && opt->srcrt)
+ udh.daddr = daddr;
+
udh.uh.source = sk->sport;
- udh.uh.len = htons(len);
+ udh.uh.len = len < 0x1000 ? htons(len) : 0;
udh.uh.check = 0;
udh.iov = msg->msg_iov;
udh.wcheck = 0;
@@ -783,7 +885,7 @@
datagram_poll, /* poll */
udp_ioctl, /* ioctl */
NULL, /* init */
- NULL, /* destroy */
+ inet6_destroy_sock, /* destroy */
NULL, /* shutdown */
ipv6_setsockopt, /* setsockopt */
ipv6_getsockopt, /* getsockopt */
FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen, slshen@lbl.gov