11da177e4SLinus Torvalds /* 21da177e4SLinus Torvalds * common UDP/RAW code 31da177e4SLinus Torvalds * Linux INET implementation 41da177e4SLinus Torvalds * 51da177e4SLinus Torvalds * Authors: 61da177e4SLinus Torvalds * Hideaki YOSHIFUJI <yoshfuji@linux-ipv6.org> 71da177e4SLinus Torvalds * 81da177e4SLinus Torvalds * This program is free software; you can redistribute it and/or 91da177e4SLinus Torvalds * modify it under the terms of the GNU General Public License 101da177e4SLinus Torvalds * as published by the Free Software Foundation; either version 111da177e4SLinus Torvalds * 2 of the License, or (at your option) any later version. 121da177e4SLinus Torvalds */ 131da177e4SLinus Torvalds 141da177e4SLinus Torvalds #include <linux/types.h> 151da177e4SLinus Torvalds #include <linux/module.h> 161da177e4SLinus Torvalds #include <linux/ip.h> 171da177e4SLinus Torvalds #include <linux/in.h> 1820380731SArnaldo Carvalho de Melo #include <net/ip.h> 191da177e4SLinus Torvalds #include <net/sock.h> 201da177e4SLinus Torvalds #include <net/route.h> 21c752f073SArnaldo Carvalho de Melo #include <net/tcp_states.h> 221da177e4SLinus Torvalds 2303645a11SEric Dumazet int __ip4_datagram_connect(struct sock *sk, struct sockaddr *uaddr, int addr_len) 241da177e4SLinus Torvalds { 251da177e4SLinus Torvalds struct inet_sock *inet = inet_sk(sk); 261da177e4SLinus Torvalds struct sockaddr_in *usin = (struct sockaddr_in *) uaddr; 273038eeacSDavid S. Miller struct flowi4 *fl4; 281da177e4SLinus Torvalds struct rtable *rt; 29bada8adcSAl Viro __be32 saddr; 301da177e4SLinus Torvalds int oif; 311da177e4SLinus Torvalds int err; 321da177e4SLinus Torvalds 331da177e4SLinus Torvalds 341da177e4SLinus Torvalds if (addr_len < sizeof(*usin)) 351da177e4SLinus Torvalds return -EINVAL; 361da177e4SLinus Torvalds 371da177e4SLinus Torvalds if (usin->sin_family != AF_INET) 381da177e4SLinus Torvalds return -EAFNOSUPPORT; 391da177e4SLinus Torvalds 401da177e4SLinus Torvalds sk_dst_reset(sk); 411da177e4SLinus Torvalds 421da177e4SLinus Torvalds oif = sk->sk_bound_dev_if; 43c720c7e8SEric Dumazet saddr = inet->inet_saddr; 44f97c1e0cSJoe Perches if (ipv4_is_multicast(usin->sin_addr.s_addr)) { 451da177e4SLinus Torvalds if (!oif) 461da177e4SLinus Torvalds oif = inet->mc_index; 471da177e4SLinus Torvalds if (!saddr) 481da177e4SLinus Torvalds saddr = inet->mc_addr; 491da177e4SLinus Torvalds } 503038eeacSDavid S. Miller fl4 = &inet->cork.fl.u.ip4; 513038eeacSDavid S. Miller rt = ip_route_connect(fl4, usin->sin_addr.s_addr, saddr, 521da177e4SLinus Torvalds RT_CONN_FLAGS(sk), oif, 531da177e4SLinus Torvalds sk->sk_protocol, 540e0d44abSSteffen Klassert inet->inet_sport, usin->sin_port, sk); 55b23dd4feSDavid S. Miller if (IS_ERR(rt)) { 56b23dd4feSDavid S. Miller err = PTR_ERR(rt); 57584bdf8cSWei Dong if (err == -ENETUNREACH) 58c9e90429SEric Dumazet IP_INC_STATS(sock_net(sk), IPSTATS_MIB_OUTNOROUTES); 593038eeacSDavid S. Miller goto out; 60584bdf8cSWei Dong } 61584bdf8cSWei Dong 621da177e4SLinus Torvalds if ((rt->rt_flags & RTCF_BROADCAST) && !sock_flag(sk, SOCK_BROADCAST)) { 631da177e4SLinus Torvalds ip_rt_put(rt); 643038eeacSDavid S. Miller err = -EACCES; 653038eeacSDavid S. Miller goto out; 661da177e4SLinus Torvalds } 67c720c7e8SEric Dumazet if (!inet->inet_saddr) 683038eeacSDavid S. Miller inet->inet_saddr = fl4->saddr; /* Update source address */ 69719f8358SEric Dumazet if (!inet->inet_rcv_saddr) { 703038eeacSDavid S. Miller inet->inet_rcv_saddr = fl4->saddr; 71719f8358SEric Dumazet if (sk->sk_prot->rehash) 72719f8358SEric Dumazet sk->sk_prot->rehash(sk); 73719f8358SEric Dumazet } 743038eeacSDavid S. Miller inet->inet_daddr = fl4->daddr; 75c720c7e8SEric Dumazet inet->inet_dport = usin->sin_port; 761da177e4SLinus Torvalds sk->sk_state = TCP_ESTABLISHED; 77b73c3d0eSTom Herbert inet_set_txhash(sk); 78c720c7e8SEric Dumazet inet->inet_id = jiffies; 791da177e4SLinus Torvalds 80d8d1f30bSChangli Gao sk_dst_set(sk, &rt->dst); 813038eeacSDavid S. Miller err = 0; 823038eeacSDavid S. Miller out: 833038eeacSDavid S. Miller return err; 841da177e4SLinus Torvalds } 8503645a11SEric Dumazet EXPORT_SYMBOL(__ip4_datagram_connect); 8603645a11SEric Dumazet 8703645a11SEric Dumazet int ip4_datagram_connect(struct sock *sk, struct sockaddr *uaddr, int addr_len) 8803645a11SEric Dumazet { 8903645a11SEric Dumazet int res; 9003645a11SEric Dumazet 9103645a11SEric Dumazet lock_sock(sk); 9203645a11SEric Dumazet res = __ip4_datagram_connect(sk, uaddr, addr_len); 9303645a11SEric Dumazet release_sock(sk); 9403645a11SEric Dumazet return res; 9503645a11SEric Dumazet } 961da177e4SLinus Torvalds EXPORT_SYMBOL(ip4_datagram_connect); 978141ed9fSSteffen Klassert 989709674eSEric Dumazet /* Because UDP xmit path can manipulate sk_dst_cache without holding 999709674eSEric Dumazet * socket lock, we need to use sk_dst_set() here, 1009709674eSEric Dumazet * even if we own the socket lock. 1019709674eSEric Dumazet */ 1028141ed9fSSteffen Klassert void ip4_datagram_release_cb(struct sock *sk) 1038141ed9fSSteffen Klassert { 1048141ed9fSSteffen Klassert const struct inet_sock *inet = inet_sk(sk); 1058141ed9fSSteffen Klassert const struct ip_options_rcu *inet_opt; 1068141ed9fSSteffen Klassert __be32 daddr = inet->inet_daddr; 1079709674eSEric Dumazet struct dst_entry *dst; 1088141ed9fSSteffen Klassert struct flowi4 fl4; 1098141ed9fSSteffen Klassert struct rtable *rt; 1108141ed9fSSteffen Klassert 1118141ed9fSSteffen Klassert rcu_read_lock(); 1129709674eSEric Dumazet 1139709674eSEric Dumazet dst = __sk_dst_get(sk); 1149709674eSEric Dumazet if (!dst || !dst->obsolete || dst->ops->check(dst, 0)) { 1159709674eSEric Dumazet rcu_read_unlock(); 1169709674eSEric Dumazet return; 1179709674eSEric Dumazet } 1188141ed9fSSteffen Klassert inet_opt = rcu_dereference(inet->inet_opt); 1198141ed9fSSteffen Klassert if (inet_opt && inet_opt->opt.srr) 1208141ed9fSSteffen Klassert daddr = inet_opt->opt.faddr; 1218141ed9fSSteffen Klassert rt = ip_route_output_ports(sock_net(sk), &fl4, sk, daddr, 1228141ed9fSSteffen Klassert inet->inet_saddr, inet->inet_dport, 1238141ed9fSSteffen Klassert inet->inet_sport, sk->sk_protocol, 1248141ed9fSSteffen Klassert RT_CONN_FLAGS(sk), sk->sk_bound_dev_if); 1259709674eSEric Dumazet 1269709674eSEric Dumazet dst = !IS_ERR(rt) ? &rt->dst : NULL; 1279709674eSEric Dumazet sk_dst_set(sk, dst); 1289709674eSEric Dumazet 1298141ed9fSSteffen Klassert rcu_read_unlock(); 1308141ed9fSSteffen Klassert } 1318141ed9fSSteffen Klassert EXPORT_SYMBOL_GPL(ip4_datagram_release_cb); 132