diff options
author | Ralf Baechle <ralf@linux-mips.org> | 2000-03-07 15:45:24 +0000 |
---|---|---|
committer | Ralf Baechle <ralf@linux-mips.org> | 2000-03-07 15:45:24 +0000 |
commit | 9f9f3e6e8548a596697778337110a423c384b6f3 (patch) | |
tree | 5dd4b290ef532cf5ecb058e1a92cd3435afeac8c /net/ipv6 | |
parent | d5c9a365ee7d2fded249aa5abfc5e89587583029 (diff) |
Merge with Linux 2.3.49.
Diffstat (limited to 'net/ipv6')
-rw-r--r-- | net/ipv6/af_inet6.c | 29 | ||||
-rw-r--r-- | net/ipv6/datagram.c | 14 | ||||
-rw-r--r-- | net/ipv6/icmp.c | 4 | ||||
-rw-r--r-- | net/ipv6/ip6_input.c | 33 | ||||
-rw-r--r-- | net/ipv6/ip6_output.c | 117 | ||||
-rw-r--r-- | net/ipv6/ipv6_sockglue.c | 21 | ||||
-rw-r--r-- | net/ipv6/protocol.c | 17 | ||||
-rw-r--r-- | net/ipv6/raw.c | 43 | ||||
-rw-r--r-- | net/ipv6/tcp_ipv6.c | 39 | ||||
-rw-r--r-- | net/ipv6/udp.c | 38 |
10 files changed, 284 insertions, 71 deletions
diff --git a/net/ipv6/af_inet6.c b/net/ipv6/af_inet6.c index 09b9c1a11..919abf4f9 100644 --- a/net/ipv6/af_inet6.c +++ b/net/ipv6/af_inet6.c @@ -7,7 +7,10 @@ * * Adapted from linux/net/ipv4/af_inet.c * - * $Id: af_inet6.c,v 1.54 2000/02/12 23:34:45 davem Exp $ + * $Id: af_inet6.c,v 1.55 2000/02/27 19:51:47 davem Exp $ + * + * Fixes: + * Hideaki YOSHIFUJI : sin6_scope_id support * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -220,9 +223,8 @@ static int inet6_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len) if(sk->prot->bind) return sk->prot->bind(sk, uaddr, addr_len); - if (addr_len < sizeof(struct sockaddr_in6)) + if (addr_len < SIN6_LEN_RFC2133) return -EINVAL; - addr_type = ipv6_addr_type(&addr->sin6_addr); if ((addr_type & IPV6_ADDR_MULTICAST) && sock->type == SOCK_STREAM) return -EINVAL; @@ -258,6 +260,22 @@ static int inet6_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len) return -EINVAL; } + if (addr_type & IPV6_ADDR_LINKLOCAL) { + if (addr_len >= sizeof(struct sockaddr_in6) && + addr->sin6_scope_id) { + /* Override any existing binding, if another one + * is supplied by user. + */ + sk->bound_dev_if = addr->sin6_scope_id; + } + + /* Binding to link-local address requires an interface */ + if (sk->bound_dev_if == 0) { + release_sock(sk); + return -EINVAL; + } + } + sk->rcv_saddr = v4addr; sk->saddr = v4addr; @@ -338,6 +356,7 @@ static int inet6_getname(struct socket *sock, struct sockaddr *uaddr, sin->sin6_family = AF_INET6; sin->sin6_flowinfo = 0; + sin->sin6_scope_id = 0; if (peer) { if (!sk->dport) return -ENOTCONN; @@ -360,7 +379,9 @@ static int inet6_getname(struct socket *sock, struct sockaddr *uaddr, sin->sin6_port = sk->sport; } - *uaddr_len = sizeof(*sin); + if (ipv6_addr_type(&sin->sin6_addr) & IPV6_ADDR_LINKLOCAL) + sin->sin6_scope_id = sk->bound_dev_if; + *uaddr_len = sizeof(*sin); return(0); } diff --git a/net/ipv6/datagram.c b/net/ipv6/datagram.c index c561d318d..844ea8228 100644 --- a/net/ipv6/datagram.c +++ b/net/ipv6/datagram.c @@ -5,7 +5,7 @@ * Authors: * Pedro Roque <roque@di.fc.ul.pt> * - * $Id: datagram.c,v 1.18 1999/08/20 11:06:17 davem Exp $ + * $Id: datagram.c,v 1.19 2000/02/27 19:51:47 davem Exp $ * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -134,14 +134,20 @@ int ipv6_recv_error(struct sock *sk, struct msghdr *msg, int len) sin->sin6_family = AF_INET6; sin->sin6_flowinfo = 0; sin->sin6_port = serr->port; + sin->sin6_scope_id = 0; if (serr->ee.ee_origin == SO_EE_ORIGIN_ICMP6) { memcpy(&sin->sin6_addr, skb->nh.raw + serr->addr_offset, 16); if (sk->net_pinfo.af_inet6.sndflow) sin->sin6_flowinfo = *(u32*)(skb->nh.raw + serr->addr_offset - 24) & IPV6_FLOWINFO_MASK; - } else + if (ipv6_addr_type(&sin->sin6_addr) & IPV6_ADDR_LINKLOCAL) { + struct inet6_skb_parm *opt = (struct inet6_skb_parm *) skb->cb; + sin->sin6_scope_id = opt->iif; + } + } else { ipv6_addr_set(&sin->sin6_addr, 0, 0, __constant_htonl(0xffff), *(u32*)(skb->nh.raw + serr->addr_offset)); + } } memcpy(&errhdr.ee, &serr->ee, sizeof(struct sock_extended_err)); @@ -154,6 +160,10 @@ int ipv6_recv_error(struct sock *sk, struct msghdr *msg, int len) memcpy(&sin->sin6_addr, &skb->nh.ipv6h->saddr, 16); if (sk->net_pinfo.af_inet6.rxopt.all) datagram_recv_ctl(sk, msg, skb); + if (ipv6_addr_type(&sin->sin6_addr) & IPV6_ADDR_LINKLOCAL) { + struct inet6_skb_parm *opt = (struct inet6_skb_parm *) skb->cb; + sin->sin6_scope_id = opt->iif; + } } else { ipv6_addr_set(&sin->sin6_addr, 0, 0, __constant_htonl(0xffff), diff --git a/net/ipv6/icmp.c b/net/ipv6/icmp.c index f1c211532..ffb0787e8 100644 --- a/net/ipv6/icmp.c +++ b/net/ipv6/icmp.c @@ -5,7 +5,7 @@ * Authors: * Pedro Roque <roque@di.fc.ul.pt> * - * $Id: icmp.c,v 1.26 2000/01/19 04:06:19 davem Exp $ + * $Id: icmp.c,v 1.27 2000/02/22 23:54:28 davem Exp $ * * Based on net/ipv4/icmp.c * @@ -477,7 +477,6 @@ static void icmpv6_notify(struct sk_buff *skb, hash = nexthdr & (MAX_INET_PROTOS - 1); - read_lock(&inet6_protocol_lock); for (ipprot = (struct inet6_protocol *) inet6_protos[hash]; ipprot != NULL; ipprot=(struct inet6_protocol *)ipprot->next) { @@ -487,7 +486,6 @@ static void icmpv6_notify(struct sk_buff *skb, if (ipprot->err_handler) ipprot->err_handler(skb, hdr, NULL, type, code, pb, info); } - read_unlock(&inet6_protocol_lock); read_lock(&raw_v6_lock); if ((sk = raw_v6_htable[hash]) != NULL) { diff --git a/net/ipv6/ip6_input.c b/net/ipv6/ip6_input.c index 709443749..6c6ae227f 100644 --- a/net/ipv6/ip6_input.c +++ b/net/ipv6/ip6_input.c @@ -6,7 +6,7 @@ * Pedro Roque <roque@di.fc.ul.pt> * Ian P. Morris <I.P.Morris@soton.ac.uk> * - * $Id: ip6_input.c,v 1.15 2000/01/09 02:19:54 davem Exp $ + * $Id: ip6_input.c,v 1.17 2000/02/27 19:42:53 davem Exp $ * * Based in linux/net/ipv4/ip_input.c * @@ -26,6 +26,9 @@ #include <linux/in6.h> #include <linux/icmpv6.h> +#include <linux/netfilter.h> +#include <linux/netfilter_ipv6.h> + #include <net/sock.h> #include <net/snmp.h> @@ -38,6 +41,16 @@ #include <net/addrconf.h> + +static inline int ip6_rcv_finish( struct sk_buff *skb) +{ + + if (skb->dst == NULL) + ip6_route_input(skb); + + return skb->dst->input(skb); +} + int ipv6_rcv(struct sk_buff *skb, struct net_device *dev, struct packet_type *pt) { struct ipv6hdr *hdr; @@ -77,12 +90,7 @@ int ipv6_rcv(struct sk_buff *skb, struct net_device *dev, struct packet_type *pt return 0; } } - - if (skb->dst == NULL) - ip6_route_input(skb); - - return skb->dst->input(skb); - + return NF_HOOK(PF_INET6,NF_IP6_PRE_ROUTING, skb, dev, NULL, ip6_rcv_finish); truncated: IP6_INC_STATS_BH(Ip6InTruncatedPkts); err: @@ -97,7 +105,8 @@ out: * Deliver the packet to the host */ -int ip6_input(struct sk_buff *skb) + +static inline int ip6_input_finish(struct sk_buff *skb) { struct ipv6hdr *hdr = skb->nh.ipv6h; struct inet6_protocol *ipprot; @@ -147,7 +156,6 @@ int ip6_input(struct sk_buff *skb) raw_sk = ipv6_raw_deliver(skb, nexthdr, len); hash = nexthdr & (MAX_INET_PROTOS - 1); - read_lock(&inet6_protocol_lock); for (ipprot = (struct inet6_protocol *) inet6_protos[hash]; ipprot != NULL; ipprot = (struct inet6_protocol *) ipprot->next) { @@ -163,7 +171,6 @@ int ip6_input(struct sk_buff *skb) ipprot->handler(buff, len); found = 1; } - read_unlock(&inet6_protocol_lock); if (raw_sk) { rawv6_rcv(raw_sk, skb, len); @@ -182,6 +189,12 @@ int ip6_input(struct sk_buff *skb) return 0; } + +int ip6_input(struct sk_buff *skb) +{ + return NF_HOOK(PF_INET6,NF_IP6_LOCAL_IN, skb, skb->dev, NULL, ip6_input_finish); +} + int ip6_mc_input(struct sk_buff *skb) { struct ipv6hdr *hdr; diff --git a/net/ipv6/ip6_output.c b/net/ipv6/ip6_output.c index d902692bd..4d124d558 100644 --- a/net/ipv6/ip6_output.c +++ b/net/ipv6/ip6_output.c @@ -5,7 +5,7 @@ * Authors: * Pedro Roque <roque@di.fc.ul.pt> * - * $Id: ip6_output.c,v 1.24 2000/01/09 02:19:49 davem Exp $ + * $Id: ip6_output.c,v 1.26 2000/03/01 02:58:12 davem Exp $ * * Based on linux/net/ipv4/ip_output.c * @@ -24,6 +24,7 @@ * H. von Brand : Added missing #include <linux/string.h> */ +#include <linux/config.h> #include <linux/errno.h> #include <linux/types.h> #include <linux/string.h> @@ -34,6 +35,9 @@ #include <linux/in6.h> #include <linux/route.h> +#include <linux/netfilter.h> +#include <linux/netfilter_ipv6.h> + #include <net/sock.h> #include <net/snmp.h> @@ -57,11 +61,44 @@ static __inline__ void ipv6_select_ident(struct sk_buff *skb, struct frag_hdr *f spin_unlock_bh(&ip6_id_lock); } +static inline int ip6_output_finish(struct sk_buff *skb) +{ + + struct dst_entry *dst = skb->dst; + struct hh_cache *hh = dst->hh; + + if (hh) { + read_lock_bh(&hh->hh_lock); + memcpy(skb->data - 16, hh->hh_data, 16); + read_unlock_bh(&hh->hh_lock); + skb_push(skb, hh->hh_len); + return hh->hh_output(skb); + } else if (dst->neighbour) + return dst->neighbour->output(skb); + + kfree_skb(skb); + return -EINVAL; + +} + +/* dev_loopback_xmit for use with netfilter. */ +static int ip6_dev_loopback_xmit(struct sk_buff *newskb) +{ + newskb->mac.raw = newskb->data; + skb_pull(newskb, newskb->nh.raw - newskb->data); + newskb->pkt_type = PACKET_LOOPBACK; + newskb->ip_summed = CHECKSUM_UNNECESSARY; + BUG_TRAP(newskb->dst); + + netif_rx(newskb); + return 0; +} + + int ip6_output(struct sk_buff *skb) { struct dst_entry *dst = skb->dst; struct net_device *dev = dst->dev; - struct hh_cache *hh = dst->hh; skb->protocol = __constant_htons(ETH_P_IPV6); skb->dev = dev; @@ -70,10 +107,15 @@ int ip6_output(struct sk_buff *skb) if (!(dev->flags&IFF_LOOPBACK) && (skb->sk == NULL || skb->sk->net_pinfo.af_inet6.mc_loop) && ipv6_chk_mcast_addr(dev, &skb->nh.ipv6h->daddr)) { + struct sk_buff *newskb = skb_clone(skb, GFP_ATOMIC); + /* Do not check for IFF_ALLMULTI; multicast routing is not supported in any case. */ - dev_loopback_xmit(skb); + if (newskb) + NF_HOOK(PF_INET, NF_IP6_POST_ROUTING, newskb, NULL, + newskb->dev, + ip6_dev_loopback_xmit); if (skb->nh.ipv6h->hop_limit == 0) { kfree_skb(skb); @@ -84,17 +126,51 @@ int ip6_output(struct sk_buff *skb) IP6_INC_STATS(Ip6OutMcastPkts); } - if (hh) { - read_lock_bh(&hh->hh_lock); - memcpy(skb->data - 16, hh->hh_data, 16); - read_unlock_bh(&hh->hh_lock); - skb_push(skb, hh->hh_len); - return hh->hh_output(skb); - } else if (dst->neighbour) - return dst->neighbour->output(skb); + return NF_HOOK(PF_INET6, NF_IP6_POST_ROUTING, skb,NULL, skb->dev,ip6_output_finish); +} - kfree_skb(skb); - return -EINVAL; + +#ifdef CONFIG_NETFILTER +static int route6_me_harder(struct sk_buff *skb) +{ + struct ipv6hdr *iph = skb->nh.ipv6h; + struct dst_entry *dst; + struct flowi fl; + + fl.proto = iph->nexthdr; + fl.fl6_dst = &iph->daddr; + fl.fl6_src = &iph->saddr; + fl.oif = skb->sk ? skb->sk->bound_dev_if : 0; + fl.fl6_flowlabel = 0; + fl.uli_u.ports.dport = 0; + fl.uli_u.ports.sport = 0; + + dst = ip6_route_output(skb->sk, &fl); + + if (dst->error) { + printk(KERN_DEBUG "route6_me_harder: No more route.\n"); + return -EINVAL; + } + + /* Drop old route. */ + dst_release(skb->dst); + + skb->dst = dst; + return 0; +} +#endif + +static inline int ip6_maybe_reroute(struct sk_buff *skb) +{ +#ifdef CONFIG_NETFILTER + if (skb->nfcache & NFC_ALTERED){ + if (route6_me_harder(skb) != 0){ + kfree_skb(skb); + return -EINVAL; + } + } +#endif /* CONFIG_NETFILTER */ + return skb->dst->output(skb); } /* @@ -159,7 +235,7 @@ int ip6_xmit(struct sock *sk, struct sk_buff *skb, struct flowi *fl, if (skb->len <= dst->pmtu) { IP6_INC_STATS(Ip6OutRequests); - return dst->output(skb); + return NF_HOOK(PF_INET6, NF_IP6_LOCAL_OUT, skb, NULL, dst->dev, ip6_maybe_reroute); } printk(KERN_DEBUG "IPv6: sending pkt_too_big to self\n"); @@ -388,7 +464,7 @@ static int ip6_frag_xmit(struct sock *sk, inet_getfrag_t getfrag, IP6_INC_STATS(Ip6FragCreates); IP6_INC_STATS(Ip6OutRequests); - err = dst->output(skb); + err = NF_HOOK(PF_INET6,NF_IP6_LOCAL_OUT, skb, NULL, dst->dev, ip6_maybe_reroute); if (err) { kfree_skb(last_skb); return err; @@ -414,7 +490,7 @@ static int ip6_frag_xmit(struct sock *sk, inet_getfrag_t getfrag, IP6_INC_STATS(Ip6FragCreates); IP6_INC_STATS(Ip6FragOKs); IP6_INC_STATS(Ip6OutRequests); - return dst->output(last_skb); + return NF_HOOK(PF_INET6, NF_IP6_LOCAL_OUT, last_skb, NULL,dst->dev, ip6_maybe_reroute); } int ip6_build_xmit(struct sock *sk, inet_getfrag_t getfrag, const void *data, @@ -582,7 +658,7 @@ int ip6_build_xmit(struct sock *sk, inet_getfrag_t getfrag, const void *data, if (!err) { IP6_INC_STATS(Ip6OutRequests); - err = dst->output(skb); + err = NF_HOOK(PF_INET6, NF_IP6_LOCAL_OUT, skb, NULL, dst->dev, ip6_maybe_reroute); } else { err = -EFAULT; kfree_skb(skb); @@ -636,6 +712,11 @@ int ip6_call_ra_chain(struct sk_buff *skb, int sel) return 0; } +static inline int ip6_forward_finish(struct sk_buff *skb) +{ + return skb->dst->output(skb); +} + int ip6_forward(struct sk_buff *skb) { struct dst_entry *dst = skb->dst; @@ -726,7 +807,7 @@ int ip6_forward(struct sk_buff *skb) hdr->hop_limit--; IP6_INC_STATS_BH(Ip6OutForwDatagrams); - return dst->output(skb); + return NF_HOOK(PF_INET6,NF_IP6_FORWARD, skb, skb->dev, dst->dev, ip6_forward_finish); drop: IP6_INC_STATS_BH(Ip6InAddrErrors); diff --git a/net/ipv6/ipv6_sockglue.c b/net/ipv6/ipv6_sockglue.c index 9f13435fa..87c9f1eb4 100644 --- a/net/ipv6/ipv6_sockglue.c +++ b/net/ipv6/ipv6_sockglue.c @@ -7,7 +7,7 @@ * * Based on linux/net/ipv4/ip_sockglue.c * - * $Id: ipv6_sockglue.c,v 1.32 2000/01/31 01:21:25 davem Exp $ + * $Id: ipv6_sockglue.c,v 1.33 2000/02/27 19:42:54 davem Exp $ * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -35,6 +35,7 @@ #include <linux/if_arp.h> #include <linux/init.h> #include <linux/sysctl.h> +#include <linux/netfilter.h> #include <net/sock.h> #include <net/snmp.h> @@ -371,6 +372,14 @@ done: case IPV6_FLOWLABEL_MGR: retv = ipv6_flowlabel_opt(sk, optval, optlen); break; + +#ifdef CONFIG_NETFILTER + default: + retv = nf_setsockopt(sk, PF_INET6, optname, optval, + optlen); + break; +#endif + } release_sock(sk); @@ -450,7 +459,17 @@ int ipv6_getsockopt(struct sock *sk, int level, int optname, char *optval, break; } default: +#ifdef CONFIG_NETFILTER + lock_sock(sk); + val = nf_getsockopt(sk, PF_INET6, optname, optval, + &len); + release_sock(sk); + if (val >= 0) + val = put_user(len, optlen); + return val; +#else return -EINVAL; +#endif } len=min(sizeof(int),len); if(put_user(len, optlen)) diff --git a/net/ipv6/protocol.c b/net/ipv6/protocol.c index c30e751ed..accc87cbe 100644 --- a/net/ipv6/protocol.c +++ b/net/ipv6/protocol.c @@ -5,7 +5,7 @@ * * PF_INET6 protocol dispatch tables. * - * Version: $Id: protocol.c,v 1.7 1999/08/20 11:06:26 davem Exp $ + * Version: $Id: protocol.c,v 1.8 2000/02/22 23:54:29 davem Exp $ * * Authors: Pedro Roque <roque@di.fc.ul.pt> * @@ -24,6 +24,7 @@ #include <linux/in6.h> #include <linux/netdevice.h> #include <linux/if_arp.h> +#include <linux/brlock.h> #include <net/sock.h> #include <net/snmp.h> @@ -37,15 +38,13 @@ struct inet6_protocol *inet6_protos[MAX_INET_PROTOS] = NULL }; -rwlock_t inet6_protocol_lock = RW_LOCK_UNLOCKED; - void inet6_add_protocol(struct inet6_protocol *prot) { unsigned char hash; struct inet6_protocol *p2; hash = prot->protocol & (MAX_INET_PROTOS - 1); - write_lock_bh(&inet6_protocol_lock); + br_write_lock_bh(BR_NETPROTO_LOCK); prot->next = inet6_protos[hash]; inet6_protos[hash] = prot; prot->copy = 0; @@ -62,7 +61,7 @@ void inet6_add_protocol(struct inet6_protocol *prot) } p2 = (struct inet6_protocol *) p2->next; } - write_unlock_bh(&inet6_protocol_lock); + br_write_unlock_bh(BR_NETPROTO_LOCK); } /* @@ -76,10 +75,10 @@ int inet6_del_protocol(struct inet6_protocol *prot) unsigned char hash; hash = prot->protocol & (MAX_INET_PROTOS - 1); - write_lock_bh(&inet6_protocol_lock); + br_write_lock_bh(BR_NETPROTO_LOCK); if (prot == inet6_protos[hash]) { inet6_protos[hash] = (struct inet6_protocol *) inet6_protos[hash]->next; - write_unlock_bh(&inet6_protocol_lock); + br_write_unlock_bh(BR_NETPROTO_LOCK); return(0); } @@ -98,7 +97,7 @@ int inet6_del_protocol(struct inet6_protocol *prot) if (p->copy == 0 && lp != NULL) lp->copy = 0; p->next = prot->next; - write_unlock_bh(&inet6_protocol_lock); + br_write_unlock_bh(BR_NETPROTO_LOCK); return(0); } if (p->next != NULL && p->next->protocol == prot->protocol) @@ -106,6 +105,6 @@ int inet6_del_protocol(struct inet6_protocol *prot) p = (struct inet6_protocol *) p->next; } - write_unlock_bh(&inet6_protocol_lock); + br_write_unlock_bh(BR_NETPROTO_LOCK); return(-1); } diff --git a/net/ipv6/raw.c b/net/ipv6/raw.c index 574bc165c..bb4ecb551 100644 --- a/net/ipv6/raw.c +++ b/net/ipv6/raw.c @@ -7,7 +7,10 @@ * * Adapted from linux/net/ipv4/raw.c * - * $Id: raw.c,v 1.33 2000/01/18 08:24:22 davem Exp $ + * $Id: raw.c,v 1.34 2000/02/27 19:51:48 davem Exp $ + * + * Fixes: + * Hideaki YOSHIFUJI : sin6_scope_id support * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -183,9 +186,8 @@ static int rawv6_bind(struct sock *sk, struct sockaddr *uaddr, int addr_len) int addr_type; int err; - if (addr_len < sizeof(struct sockaddr_in6)) + if (addr_len < SIN6_LEN_RFC2133) return -EINVAL; - addr_type = ipv6_addr_type(&addr->sin6_addr); /* Raw sockets are IPv6 only */ @@ -198,6 +200,20 @@ static int rawv6_bind(struct sock *sk, struct sockaddr *uaddr, int addr_len) if (sk->state != TCP_CLOSE) goto out; + if (addr_type & IPV6_ADDR_LINKLOCAL) { + if (addr_len >= sizeof(struct sockaddr_in6) && + addr->sin6_scope_id) { + /* Override any existing binding, if another one + * is supplied by user. + */ + sk->bound_dev_if = addr->sin6_scope_id; + } + + /* Binding to link-local address requires an interface */ + if (sk->bound_dev_if == 0) + goto out; + } + /* Check if the address belongs to the host. */ if (addr_type != IPV6_ADDR_ANY) { /* ipv4 addr of the socket is invalid. Only the @@ -325,6 +341,11 @@ int rawv6_recvmsg(struct sock *sk, struct msghdr *msg, int len, memcpy(&sin6->sin6_addr, &skb->nh.ipv6h->saddr, sizeof(struct in6_addr)); sin6->sin6_flowinfo = 0; + sin6->sin6_scope_id = 0; + if (ipv6_addr_type(&sin6->sin6_addr) & IPV6_ADDR_LINKLOCAL) { + struct inet6_skb_parm *opt = (struct inet6_skb_parm *) skb->cb; + sin6->sin6_scope_id = opt->iif; + } } if (sk->net_pinfo.af_inet6.rxopt.all) @@ -429,14 +450,15 @@ static int rawv6_sendmsg(struct sock *sk, struct msghdr *msg, int len) */ fl.fl6_flowlabel = 0; + fl.oif = 0; if (sin6) { - if (addr_len < sizeof(struct sockaddr_in6)) - return(-EINVAL); + if (addr_len < SIN6_LEN_RFC2133) + return -EINVAL; if (sin6->sin6_family && sin6->sin6_family != AF_INET6) return(-EINVAL); - + /* port is the proto value [0..255] carried in nexthdr */ proto = ntohs(sin6->sin6_port); @@ -457,11 +479,15 @@ static int rawv6_sendmsg(struct sock *sk, struct msghdr *msg, int len) } } - /* Otherwise it will be difficult to maintain sk->dst_cache. */ if (sk->state == TCP_ESTABLISHED && !ipv6_addr_cmp(daddr, &sk->net_pinfo.af_inet6.daddr)) daddr = &sk->net_pinfo.af_inet6.daddr; + + if (addr_len >= sizeof(struct sockaddr_in6) && + sin6->sin6_scope_id && + ipv6_addr_type(daddr)&IPV6_ADDR_LINKLOCAL) + fl.oif = sin6->sin6_scope_id; } else { if (sk->state != TCP_ESTABLISHED) return(-EINVAL); @@ -479,7 +505,8 @@ static int rawv6_sendmsg(struct sock *sk, struct msghdr *msg, int len) return(-EINVAL); } - fl.oif = sk->bound_dev_if; + if (fl.oif == 0) + fl.oif = sk->bound_dev_if; fl.fl6_src = NULL; if (msg->msg_controllen) { diff --git a/net/ipv6/tcp_ipv6.c b/net/ipv6/tcp_ipv6.c index 47dcf8ce0..f47b4a103 100644 --- a/net/ipv6/tcp_ipv6.c +++ b/net/ipv6/tcp_ipv6.c @@ -5,13 +5,16 @@ * Authors: * Pedro Roque <roque@di.fc.ul.pt> * - * $Id: tcp_ipv6.c,v 1.119 2000/01/31 01:21:26 davem Exp $ + * $Id: tcp_ipv6.c,v 1.120 2000/02/27 19:51:49 davem Exp $ * * Based on: * linux/net/ipv4/tcp.c * linux/net/ipv4/tcp_input.c * linux/net/ipv4/tcp_output.c * + * Fixes: + * Hideaki YOSHIFUJI : sin6_scope_id support + * * 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 @@ -509,8 +512,8 @@ static int tcp_v6_connect(struct sock *sk, struct sockaddr *uaddr, int addr_type; int err; - if (addr_len < sizeof(struct sockaddr_in6)) - return(-EINVAL); + if (addr_len < SIN6_LEN_RFC2133) + return -EINVAL; if (usin->sin6_family != AF_INET6) return(-EAFNOSUPPORT); @@ -540,6 +543,24 @@ static int tcp_v6_connect(struct sock *sk, struct sockaddr *uaddr, if(addr_type & IPV6_ADDR_MULTICAST) return -ENETUNREACH; + if (addr_type&IPV6_ADDR_LINKLOCAL) { + if (addr_len >= sizeof(struct sockaddr_in6) && + usin->sin6_scope_id) { + /* If interface is set while binding, indices + * must coincide. + */ + if (sk->bound_dev_if && + sk->bound_dev_if != usin->sin6_scope_id) + return -EINVAL; + + sk->bound_dev_if = usin->sin6_scope_id; + } + + /* Connect to link-local address requires an interface */ + if (sk->bound_dev_if == 0) + return -EINVAL; + } + if (tp->ts_recent_stamp && ipv6_addr_cmp(&np->daddr, &usin->sin6_addr)) { tp->ts_recent = 0; tp->ts_recent_stamp = 0; @@ -605,15 +626,6 @@ static int tcp_v6_connect(struct sock *sk, struct sockaddr *uaddr, goto failure; } - if (fl.oif == 0 && addr_type&IPV6_ADDR_LINKLOCAL) { - /* Ough! This guy tries to connect to link local - * address and did not specify interface. - * Actually we should kick him out, but - * we will be patient :) --ANK - */ - sk->bound_dev_if = dst->dev->ifindex; - } - ip6_dst_store(sk, dst, NULL); if (saddr == NULL) { @@ -1723,6 +1735,9 @@ static void v6_addr2sockaddr(struct sock *sk, struct sockaddr * uaddr) sin6->sin6_port = sk->dport; /* We do not store received flowlabel for TCP */ sin6->sin6_flowinfo = 0; + sin6->sin6_scope_id = 0; + if (sk->bound_dev_if && ipv6_addr_type(&sin6->sin6_addr)&IPV6_ADDR_LINKLOCAL) + sin6->sin6_scope_id = sk->bound_dev_if; } static int tcp_v6_remember_stamp(struct sock *sk) diff --git a/net/ipv6/udp.c b/net/ipv6/udp.c index a5984354b..fed8e3aa2 100644 --- a/net/ipv6/udp.c +++ b/net/ipv6/udp.c @@ -7,7 +7,10 @@ * * Based on linux/ipv4/udp.c * - * $Id: udp.c,v 1.50 2000/01/18 08:24:24 davem Exp $ + * $Id: udp.c,v 1.51 2000/02/27 19:51:51 davem Exp $ + * + * Fixes: + * Hideaki YOSHIFUJI : sin6_scope_id support * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -218,7 +221,7 @@ int udpv6_connect(struct sock *sk, struct sockaddr *uaddr, int addr_len) goto ipv4_connected; } - if (addr_len < sizeof(*usin)) + if (addr_len < SIN6_LEN_RFC2133) return -EINVAL; if (usin->sin6_family != AF_INET6) @@ -278,6 +281,21 @@ ipv4_connected: return 0; } + if (addr_type&IPV6_ADDR_LINKLOCAL) { + if (addr_len >= sizeof(struct sockaddr_in6) && + usin->sin6_scope_id) { + if (sk->bound_dev_if && sk->bound_dev_if != usin->sin6_scope_id) { + fl6_sock_release(flowlabel); + return -EINVAL; + } + sk->bound_dev_if = usin->sin6_scope_id; + } + + /* Connect to link-local address requires an interface */ + if (sk->bound_dev_if == 0) + return -EINVAL; + } + ipv6_addr_copy(&np->daddr, daddr); np->flow_label = fl.fl6_flowlabel; @@ -392,6 +410,7 @@ int udpv6_recvmsg(struct sock *sk, struct msghdr *msg, int len, sin6->sin6_family = AF_INET6; sin6->sin6_port = skb->h.uh->source; sin6->sin6_flowinfo = 0; + sin6->sin6_scope_id = 0; if (skb->protocol == __constant_htons(ETH_P_IP)) { ipv6_addr_set(&sin6->sin6_addr, 0, 0, @@ -404,6 +423,10 @@ int udpv6_recvmsg(struct sock *sk, struct msghdr *msg, int len, if (sk->net_pinfo.af_inet6.rxopt.all) datagram_recv_ctl(sk, msg, skb); + if (ipv6_addr_type(&sin6->sin6_addr) & IPV6_ADDR_LINKLOCAL) { + struct inet6_skb_parm *opt = (struct inet6_skb_parm *) skb->cb; + sin6->sin6_scope_id = opt->iif; + } } } err = copied; @@ -746,12 +769,13 @@ static int udpv6_sendmsg(struct sock *sk, struct msghdr *msg, int ulen) return -EMSGSIZE; fl.fl6_flowlabel = 0; + fl.oif = 0; if (sin6) { if (sin6->sin6_family == AF_INET) return udp_sendmsg(sk, msg, ulen); - if (addr_len < sizeof(*sin6)) + if (addr_len < SIN6_LEN_RFC2133) return -EINVAL; if (sin6->sin6_family && sin6->sin6_family != AF_INET6) @@ -777,6 +801,11 @@ static int udpv6_sendmsg(struct sock *sk, struct msghdr *msg, int ulen) if (sk->state == TCP_ESTABLISHED && !ipv6_addr_cmp(daddr, &sk->net_pinfo.af_inet6.daddr)) daddr = &sk->net_pinfo.af_inet6.daddr; + + if (addr_len >= sizeof(struct sockaddr_in6) && + sin6->sin6_scope_id && + ipv6_addr_type(daddr)&IPV6_ADDR_LINKLOCAL) + fl.oif = sin6->sin6_scope_id; } else { if (sk->state != TCP_ESTABLISHED) return -ENOTCONN; @@ -802,7 +831,8 @@ static int udpv6_sendmsg(struct sock *sk, struct msghdr *msg, int ulen) } udh.daddr = NULL; - fl.oif = sk->bound_dev_if; + if (!fl.oif) + fl.oif = sk->bound_dev_if; fl.fl6_src = NULL; if (msg->msg_controllen) { |