Commit 587451ba authored by Hideaki Yoshifuji's avatar Hideaki Yoshifuji

[IPV4] Look up route with appropriate protocol when we connect().

Signed-off-by: default avatarHideaki YOSHIFUJI <yoshfuji@linux-ipv6.org>
parent 1bb0fa18
...@@ -109,6 +109,9 @@ extern ssize_t ip_append_page(struct sock *sk, struct page *page, ...@@ -109,6 +109,9 @@ extern ssize_t ip_append_page(struct sock *sk, struct page *page,
extern int ip_push_pending_frames(struct sock *sk); extern int ip_push_pending_frames(struct sock *sk);
extern void ip_flush_pending_frames(struct sock *sk); extern void ip_flush_pending_frames(struct sock *sk);
/* datagram.c */
extern int ip4_datagram_connect(struct sock *sk,
struct sockaddr *uaddr, int addr_len);
/* /*
* Map a multicast IP onto multicast MAC for type Token Ring. * Map a multicast IP onto multicast MAC for type Token Ring.
......
...@@ -406,6 +406,9 @@ extern void ipv6_packet_init(void); ...@@ -406,6 +406,9 @@ extern void ipv6_packet_init(void);
extern void ipv6_packet_cleanup(void); extern void ipv6_packet_cleanup(void);
extern int ip6_datagram_connect(struct sock *sk,
struct sockaddr *addr, int addr_len);
extern int ipv6_recv_error(struct sock *sk, struct msghdr *msg, int len); extern int ipv6_recv_error(struct sock *sk, struct msghdr *msg, int len);
extern void ipv6_icmp_error(struct sock *sk, struct sk_buff *skb, int err, u16 port, extern void ipv6_icmp_error(struct sock *sk, struct sk_buff *skb, int err, u16 port,
u32 info, u8 *payload); u32 info, u8 *payload);
......
...@@ -64,8 +64,6 @@ extern struct proto udp_prot; ...@@ -64,8 +64,6 @@ extern struct proto udp_prot;
extern void udp_err(struct sk_buff *, u32); extern void udp_err(struct sk_buff *, u32);
extern int udp_connect(struct sock *sk,
struct sockaddr *usin, int addr_len);
extern int udp_sendmsg(struct kiocb *iocb, struct sock *sk, extern int udp_sendmsg(struct kiocb *iocb, struct sock *sk,
struct msghdr *msg, size_t len); struct msghdr *msg, size_t len);
......
...@@ -6,7 +6,7 @@ obj-y := utils.o route.o inetpeer.o protocol.o \ ...@@ -6,7 +6,7 @@ obj-y := utils.o route.o inetpeer.o protocol.o \
ip_input.o ip_fragment.o ip_forward.o ip_options.o \ ip_input.o ip_fragment.o ip_forward.o ip_options.o \
ip_output.o ip_sockglue.o \ ip_output.o ip_sockglue.o \
tcp.o tcp_input.o tcp_output.o tcp_timer.o tcp_ipv4.o tcp_minisocks.o \ tcp.o tcp_input.o tcp_output.o tcp_timer.o tcp_ipv4.o tcp_minisocks.o \
tcp_diag.o raw.o udp.o arp.o icmp.o devinet.o af_inet.o igmp.o \ tcp_diag.o datagram.o raw.o udp.o arp.o icmp.o devinet.o af_inet.o igmp.o \
sysctl_net_ipv4.o fib_frontend.o fib_semantics.o fib_hash.o sysctl_net_ipv4.o fib_frontend.o fib_semantics.o fib_hash.o
obj-$(CONFIG_PROC_FS) += proc.o obj-$(CONFIG_PROC_FS) += proc.o
......
/*
* common UDP/RAW code
* Linux INET implementation
*
* Authors:
* Hideaki YOSHIFUJI <yoshfuji@linux-ipv6.org>
*
* 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
* 2 of the License, or (at your option) any later version.
*/
#include <linux/config.h>
#include <linux/types.h>
#include <linux/module.h>
#include <linux/ip.h>
#include <linux/in.h>
#include <net/sock.h>
#include <net/tcp.h>
#include <net/route.h>
int ip4_datagram_connect(struct sock *sk, struct sockaddr *uaddr, int addr_len)
{
struct inet_opt *inet = inet_sk(sk);
struct sockaddr_in *usin = (struct sockaddr_in *) uaddr;
struct rtable *rt;
u32 saddr;
int oif;
int err;
if (addr_len < sizeof(*usin))
return -EINVAL;
if (usin->sin_family != AF_INET)
return -EAFNOSUPPORT;
sk_dst_reset(sk);
oif = sk->sk_bound_dev_if;
saddr = inet->saddr;
if (MULTICAST(usin->sin_addr.s_addr)) {
if (!oif)
oif = inet->mc_index;
if (!saddr)
saddr = inet->mc_addr;
}
err = ip_route_connect(&rt, usin->sin_addr.s_addr, saddr,
RT_CONN_FLAGS(sk), oif,
sk->sk_protocol,
inet->sport, usin->sin_port, sk);
if (err)
return err;
if ((rt->rt_flags & RTCF_BROADCAST) && !sock_flag(sk, SOCK_BROADCAST)) {
ip_rt_put(rt);
return -EACCES;
}
if (!inet->saddr)
inet->saddr = rt->rt_src; /* Update source address */
if (!inet->rcv_saddr)
inet->rcv_saddr = rt->rt_src;
inet->daddr = rt->rt_dst;
inet->dport = usin->sin_port;
sk->sk_state = TCP_ESTABLISHED;
inet->id = jiffies;
sk_dst_set(sk, &rt->u.dst);
return(0);
}
EXPORT_SYMBOL(ip4_datagram_connect);
...@@ -657,7 +657,7 @@ static int raw_ioctl(struct sock *sk, int cmd, unsigned long arg) ...@@ -657,7 +657,7 @@ static int raw_ioctl(struct sock *sk, int cmd, unsigned long arg)
struct proto raw_prot = { struct proto raw_prot = {
.name = "RAW", .name = "RAW",
.close = raw_close, .close = raw_close,
.connect = udp_connect, .connect = ip4_datagram_connect,
.disconnect = udp_disconnect, .disconnect = udp_disconnect,
.ioctl = raw_ioctl, .ioctl = raw_ioctl,
.init = raw_init, .init = raw_init,
......
...@@ -858,54 +858,6 @@ int udp_recvmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg, ...@@ -858,54 +858,6 @@ int udp_recvmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg,
goto try_again; goto try_again;
} }
int udp_connect(struct sock *sk, struct sockaddr *uaddr, int addr_len)
{
struct inet_opt *inet = inet_sk(sk);
struct sockaddr_in *usin = (struct sockaddr_in *) uaddr;
struct rtable *rt;
u32 saddr;
int oif;
int err;
if (addr_len < sizeof(*usin))
return -EINVAL;
if (usin->sin_family != AF_INET)
return -EAFNOSUPPORT;
sk_dst_reset(sk);
oif = sk->sk_bound_dev_if;
saddr = inet->saddr;
if (MULTICAST(usin->sin_addr.s_addr)) {
if (!oif)
oif = inet->mc_index;
if (!saddr)
saddr = inet->mc_addr;
}
err = ip_route_connect(&rt, usin->sin_addr.s_addr, saddr,
RT_CONN_FLAGS(sk), oif,
IPPROTO_UDP,
inet->sport, usin->sin_port, sk);
if (err)
return err;
if ((rt->rt_flags & RTCF_BROADCAST) && !sock_flag(sk, SOCK_BROADCAST)) {
ip_rt_put(rt);
return -EACCES;
}
if (!inet->saddr)
inet->saddr = rt->rt_src; /* Update source address */
if (!inet->rcv_saddr)
inet->rcv_saddr = rt->rt_src;
inet->daddr = rt->rt_dst;
inet->dport = usin->sin_port;
sk->sk_state = TCP_ESTABLISHED;
inet->id = jiffies;
sk_dst_set(sk, &rt->u.dst);
return(0);
}
int udp_disconnect(struct sock *sk, int flags) int udp_disconnect(struct sock *sk, int flags)
{ {
...@@ -1351,7 +1303,7 @@ static int udp_getsockopt(struct sock *sk, int level, int optname, ...@@ -1351,7 +1303,7 @@ static int udp_getsockopt(struct sock *sk, int level, int optname,
struct proto udp_prot = { struct proto udp_prot = {
.name = "UDP", .name = "UDP",
.close = udp_close, .close = udp_close,
.connect = udp_connect, .connect = ip4_datagram_connect,
.disconnect = udp_disconnect, .disconnect = udp_disconnect,
.ioctl = udp_ioctl, .ioctl = udp_ioctl,
.destroy = udp_destroy_sock, .destroy = udp_destroy_sock,
...@@ -1552,7 +1504,6 @@ void udp4_proc_exit(void) ...@@ -1552,7 +1504,6 @@ void udp4_proc_exit(void)
} }
#endif /* CONFIG_PROC_FS */ #endif /* CONFIG_PROC_FS */
EXPORT_SYMBOL(udp_connect);
EXPORT_SYMBOL(udp_disconnect); EXPORT_SYMBOL(udp_disconnect);
EXPORT_SYMBOL(udp_hash); EXPORT_SYMBOL(udp_hash);
EXPORT_SYMBOL(udp_hash_lock); EXPORT_SYMBOL(udp_hash_lock);
......
...@@ -28,10 +28,166 @@ ...@@ -28,10 +28,166 @@
#include <net/ndisc.h> #include <net/ndisc.h>
#include <net/addrconf.h> #include <net/addrconf.h>
#include <net/transp_v6.h> #include <net/transp_v6.h>
#include <net/ip6_route.h>
#include <linux/errqueue.h> #include <linux/errqueue.h>
#include <asm/uaccess.h> #include <asm/uaccess.h>
int ip6_datagram_connect(struct sock *sk, struct sockaddr *uaddr, int addr_len)
{
struct sockaddr_in6 *usin = (struct sockaddr_in6 *) uaddr;
struct inet_opt *inet = inet_sk(sk);
struct ipv6_pinfo *np = inet6_sk(sk);
struct in6_addr *daddr;
struct dst_entry *dst;
struct flowi fl;
struct ip6_flowlabel *flowlabel = NULL;
int addr_type;
int err;
if (usin->sin6_family == AF_INET) {
if (__ipv6_only_sock(sk))
return -EAFNOSUPPORT;
err = ip4_datagram_connect(sk, uaddr, addr_len);
goto ipv4_connected;
}
if (addr_len < SIN6_LEN_RFC2133)
return -EINVAL;
if (usin->sin6_family != AF_INET6)
return -EAFNOSUPPORT;
memset(&fl, 0, sizeof(fl));
if (np->sndflow) {
fl.fl6_flowlabel = usin->sin6_flowinfo&IPV6_FLOWINFO_MASK;
if (fl.fl6_flowlabel&IPV6_FLOWLABEL_MASK) {
flowlabel = fl6_sock_lookup(sk, fl.fl6_flowlabel);
if (flowlabel == NULL)
return -EINVAL;
ipv6_addr_copy(&usin->sin6_addr, &flowlabel->dst);
}
}
addr_type = ipv6_addr_type(&usin->sin6_addr);
if (addr_type == IPV6_ADDR_ANY) {
/*
* connect to self
*/
usin->sin6_addr.s6_addr[15] = 0x01;
}
daddr = &usin->sin6_addr;
if (addr_type == IPV6_ADDR_MAPPED) {
struct sockaddr_in sin;
if (__ipv6_only_sock(sk)) {
err = -ENETUNREACH;
goto out;
}
sin.sin_family = AF_INET;
sin.sin_addr.s_addr = daddr->s6_addr32[3];
sin.sin_port = usin->sin6_port;
err = ip4_datagram_connect(sk,
(struct sockaddr*) &sin,
sizeof(sin));
ipv4_connected:
if (err)
goto out;
ipv6_addr_set(&np->daddr, 0, 0, htonl(0x0000ffff), inet->daddr);
if (ipv6_addr_any(&np->saddr)) {
ipv6_addr_set(&np->saddr, 0, 0, htonl(0x0000ffff),
inet->saddr);
}
if (ipv6_addr_any(&np->rcv_saddr)) {
ipv6_addr_set(&np->rcv_saddr, 0, 0, htonl(0x0000ffff),
inet->rcv_saddr);
}
goto out;
}
if (addr_type&IPV6_ADDR_LINKLOCAL) {
if (addr_len >= sizeof(struct sockaddr_in6) &&
usin->sin6_scope_id) {
if (sk->sk_bound_dev_if &&
sk->sk_bound_dev_if != usin->sin6_scope_id) {
err = -EINVAL;
goto out;
}
sk->sk_bound_dev_if = usin->sin6_scope_id;
if (!sk->sk_bound_dev_if &&
(addr_type & IPV6_ADDR_MULTICAST))
fl.oif = np->mcast_oif;
}
/* Connect to link-local address requires an interface */
if (!sk->sk_bound_dev_if) {
err = -EINVAL;
goto out;
}
}
ipv6_addr_copy(&np->daddr, daddr);
np->flow_label = fl.fl6_flowlabel;
inet->dport = usin->sin6_port;
/*
* Check for a route to destination an obtain the
* destination cache for it.
*/
fl.proto = sk->sk_protocol;
ipv6_addr_copy(&fl.fl6_dst, &np->daddr);
ipv6_addr_copy(&fl.fl6_src, &np->saddr);
fl.oif = sk->sk_bound_dev_if;
fl.fl_ip_dport = inet->dport;
fl.fl_ip_sport = inet->sport;
if (!fl.oif && (addr_type&IPV6_ADDR_MULTICAST))
fl.oif = np->mcast_oif;
if (flowlabel) {
if (flowlabel->opt && flowlabel->opt->srcrt) {
struct rt0_hdr *rt0 = (struct rt0_hdr *) flowlabel->opt->srcrt;
ipv6_addr_copy(&fl.fl6_dst, rt0->addr);
}
} else if (np->opt && np->opt->srcrt) {
struct rt0_hdr *rt0 = (struct rt0_hdr *)np->opt->srcrt;
ipv6_addr_copy(&fl.fl6_dst, rt0->addr);
}
err = ip6_dst_lookup(sk, &dst, &fl);
if (err)
goto out;
/* source address lookup done in ip6_dst_lookup */
if (ipv6_addr_any(&np->saddr))
ipv6_addr_copy(&np->saddr, &fl.fl6_src);
if (ipv6_addr_any(&np->rcv_saddr)) {
ipv6_addr_copy(&np->rcv_saddr, &fl.fl6_src);
inet->rcv_saddr = LOOPBACK4_IPV6;
}
ip6_dst_store(sk, dst,
!ipv6_addr_cmp(&fl.fl6_dst, &np->daddr) ?
&np->daddr : NULL);
sk->sk_state = TCP_ESTABLISHED;
out:
fl6_sock_release(flowlabel);
return err;
}
void ipv6_icmp_error(struct sock *sk, struct sk_buff *skb, int err, void ipv6_icmp_error(struct sock *sk, struct sk_buff *skb, int err,
u16 port, u32 info, u8 *payload) u16 port, u32 info, u8 *payload)
{ {
......
...@@ -916,7 +916,7 @@ static int rawv6_init_sk(struct sock *sk) ...@@ -916,7 +916,7 @@ static int rawv6_init_sk(struct sock *sk)
struct proto rawv6_prot = { struct proto rawv6_prot = {
.name = "RAW", .name = "RAW",
.close = rawv6_close, .close = rawv6_close,
.connect = udpv6_connect, .connect = ip6_datagram_connect,
.disconnect = udp_disconnect, .disconnect = udp_disconnect,
.ioctl = rawv6_ioctl, .ioctl = rawv6_ioctl,
.init = rawv6_init_sk, .init = rawv6_init_sk,
......
...@@ -49,6 +49,7 @@ ...@@ -49,6 +49,7 @@
#include <net/addrconf.h> #include <net/addrconf.h>
#include <net/ip.h> #include <net/ip.h>
#include <net/udp.h> #include <net/udp.h>
#include <net/raw.h>
#include <net/inet_common.h> #include <net/inet_common.h>
#include <net/ip6_checksum.h> #include <net/ip6_checksum.h>
...@@ -203,159 +204,6 @@ static struct sock *udp_v6_lookup(struct in6_addr *saddr, u16 sport, ...@@ -203,159 +204,6 @@ static struct sock *udp_v6_lookup(struct in6_addr *saddr, u16 sport,
* *
*/ */
int udpv6_connect(struct sock *sk, struct sockaddr *uaddr, int addr_len)
{
struct sockaddr_in6 *usin = (struct sockaddr_in6 *) uaddr;
struct inet_opt *inet = inet_sk(sk);
struct ipv6_pinfo *np = inet6_sk(sk);
struct in6_addr *daddr;
struct dst_entry *dst;
struct flowi fl;
struct ip6_flowlabel *flowlabel = NULL;
int addr_type;
int err;
if (usin->sin6_family == AF_INET) {
if (__ipv6_only_sock(sk))
return -EAFNOSUPPORT;
err = udp_connect(sk, uaddr, addr_len);
goto ipv4_connected;
}
if (addr_len < SIN6_LEN_RFC2133)
return -EINVAL;
if (usin->sin6_family != AF_INET6)
return -EAFNOSUPPORT;
memset(&fl, 0, sizeof(fl));
if (np->sndflow) {
fl.fl6_flowlabel = usin->sin6_flowinfo&IPV6_FLOWINFO_MASK;
if (fl.fl6_flowlabel&IPV6_FLOWLABEL_MASK) {
flowlabel = fl6_sock_lookup(sk, fl.fl6_flowlabel);
if (flowlabel == NULL)
return -EINVAL;
ipv6_addr_copy(&usin->sin6_addr, &flowlabel->dst);
}
}
addr_type = ipv6_addr_type(&usin->sin6_addr);
if (addr_type == IPV6_ADDR_ANY) {
/*
* connect to self
*/
usin->sin6_addr.s6_addr[15] = 0x01;
}
daddr = &usin->sin6_addr;
if (addr_type == IPV6_ADDR_MAPPED) {
struct sockaddr_in sin;
if (__ipv6_only_sock(sk)) {
err = -ENETUNREACH;
goto out;
}
sin.sin_family = AF_INET;
sin.sin_addr.s_addr = daddr->s6_addr32[3];
sin.sin_port = usin->sin6_port;
err = udp_connect(sk, (struct sockaddr*) &sin, sizeof(sin));
ipv4_connected:
if (err)
goto out;
ipv6_addr_set(&np->daddr, 0, 0, htonl(0x0000ffff), inet->daddr);
if (ipv6_addr_any(&np->saddr)) {
ipv6_addr_set(&np->saddr, 0, 0, htonl(0x0000ffff),
inet->saddr);
}
if (ipv6_addr_any(&np->rcv_saddr)) {
ipv6_addr_set(&np->rcv_saddr, 0, 0, htonl(0x0000ffff),
inet->rcv_saddr);
}
goto out;
}
if (addr_type&IPV6_ADDR_LINKLOCAL) {
if (addr_len >= sizeof(struct sockaddr_in6) &&
usin->sin6_scope_id) {
if (sk->sk_bound_dev_if &&
sk->sk_bound_dev_if != usin->sin6_scope_id) {
err = -EINVAL;
goto out;
}
sk->sk_bound_dev_if = usin->sin6_scope_id;
if (!sk->sk_bound_dev_if &&
(addr_type & IPV6_ADDR_MULTICAST))
fl.oif = np->mcast_oif;
}
/* Connect to link-local address requires an interface */
if (!sk->sk_bound_dev_if) {
err = -EINVAL;
goto out;
}
}
ipv6_addr_copy(&np->daddr, daddr);
np->flow_label = fl.fl6_flowlabel;
inet->dport = usin->sin6_port;
/*
* Check for a route to destination an obtain the
* destination cache for it.
*/
fl.proto = IPPROTO_UDP;
ipv6_addr_copy(&fl.fl6_dst, &np->daddr);
ipv6_addr_copy(&fl.fl6_src, &np->saddr);
fl.oif = sk->sk_bound_dev_if;
fl.fl_ip_dport = inet->dport;
fl.fl_ip_sport = inet->sport;
if (!fl.oif && (addr_type&IPV6_ADDR_MULTICAST))
fl.oif = np->mcast_oif;
if (flowlabel) {
if (flowlabel->opt && flowlabel->opt->srcrt) {
struct rt0_hdr *rt0 = (struct rt0_hdr *) flowlabel->opt->srcrt;
ipv6_addr_copy(&fl.fl6_dst, rt0->addr);
}
} else if (np->opt && np->opt->srcrt) {
struct rt0_hdr *rt0 = (struct rt0_hdr *)np->opt->srcrt;
ipv6_addr_copy(&fl.fl6_dst, rt0->addr);
}
err = ip6_dst_lookup(sk, &dst, &fl);
if (err)
goto out;
/* source address lookup done in ip6_dst_lookup */
if (ipv6_addr_any(&np->saddr))
ipv6_addr_copy(&np->saddr, &fl.fl6_src);
if (ipv6_addr_any(&np->rcv_saddr)) {
ipv6_addr_copy(&np->rcv_saddr, &fl.fl6_src);
inet->rcv_saddr = LOOPBACK4_IPV6;
}
ip6_dst_store(sk, dst,
!ipv6_addr_cmp(&fl.fl6_dst, &np->daddr) ?
&np->daddr : NULL);
sk->sk_state = TCP_ESTABLISHED;
out:
fl6_sock_release(flowlabel);
return err;
}
static void udpv6_close(struct sock *sk, long timeout) static void udpv6_close(struct sock *sk, long timeout)
{ {
sk_common_release(sk); sk_common_release(sk);
...@@ -1174,7 +1022,7 @@ void udp6_proc_exit(void) { ...@@ -1174,7 +1022,7 @@ void udp6_proc_exit(void) {
struct proto udpv6_prot = { struct proto udpv6_prot = {
.name = "UDP", .name = "UDP",
.close = udpv6_close, .close = udpv6_close,
.connect = udpv6_connect, .connect = ip6_datagram_connect,
.disconnect = udp_disconnect, .disconnect = udp_disconnect,
.ioctl = udp_ioctl, .ioctl = udp_ioctl,
.destroy = udpv6_destroy_sock, .destroy = udpv6_destroy_sock,
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment