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 231da177e4SLinus Torvalds 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 423038eeacSDavid S. Miller lock_sock(sk); 433038eeacSDavid S. Miller 441da177e4SLinus Torvalds oif = sk->sk_bound_dev_if; 45c720c7e8SEric Dumazet saddr = inet->inet_saddr; 46f97c1e0cSJoe Perches if (ipv4_is_multicast(usin->sin_addr.s_addr)) { 471da177e4SLinus Torvalds if (!oif) 481da177e4SLinus Torvalds oif = inet->mc_index; 491da177e4SLinus Torvalds if (!saddr) 501da177e4SLinus Torvalds saddr = inet->mc_addr; 511da177e4SLinus Torvalds } 523038eeacSDavid S. Miller fl4 = &inet->cork.fl.u.ip4; 533038eeacSDavid S. Miller rt = ip_route_connect(fl4, usin->sin_addr.s_addr, saddr, 541da177e4SLinus Torvalds RT_CONN_FLAGS(sk), oif, 551da177e4SLinus Torvalds sk->sk_protocol, 560e0d44abSSteffen Klassert inet->inet_sport, usin->sin_port, sk); 57b23dd4feSDavid S. Miller if (IS_ERR(rt)) { 58b23dd4feSDavid S. Miller err = PTR_ERR(rt); 59584bdf8cSWei Dong if (err == -ENETUNREACH) 60c9e90429SEric Dumazet IP_INC_STATS(sock_net(sk), IPSTATS_MIB_OUTNOROUTES); 613038eeacSDavid S. Miller goto out; 62584bdf8cSWei Dong } 63584bdf8cSWei Dong 641da177e4SLinus Torvalds if ((rt->rt_flags & RTCF_BROADCAST) && !sock_flag(sk, SOCK_BROADCAST)) { 651da177e4SLinus Torvalds ip_rt_put(rt); 663038eeacSDavid S. Miller err = -EACCES; 673038eeacSDavid S. Miller goto out; 681da177e4SLinus Torvalds } 69c720c7e8SEric Dumazet if (!inet->inet_saddr) 703038eeacSDavid S. Miller inet->inet_saddr = fl4->saddr; /* Update source address */ 71719f8358SEric Dumazet if (!inet->inet_rcv_saddr) { 723038eeacSDavid S. Miller inet->inet_rcv_saddr = fl4->saddr; 73719f8358SEric Dumazet if (sk->sk_prot->rehash) 74719f8358SEric Dumazet sk->sk_prot->rehash(sk); 75719f8358SEric Dumazet } 763038eeacSDavid S. Miller inet->inet_daddr = fl4->daddr; 77c720c7e8SEric Dumazet inet->inet_dport = usin->sin_port; 781da177e4SLinus Torvalds sk->sk_state = TCP_ESTABLISHED; 79c720c7e8SEric Dumazet inet->inet_id = jiffies; 801da177e4SLinus Torvalds 81d8d1f30bSChangli Gao sk_dst_set(sk, &rt->dst); 823038eeacSDavid S. Miller err = 0; 833038eeacSDavid S. Miller out: 843038eeacSDavid S. Miller release_sock(sk); 853038eeacSDavid S. Miller return err; 861da177e4SLinus Torvalds } 871da177e4SLinus Torvalds EXPORT_SYMBOL(ip4_datagram_connect); 888141ed9fSSteffen Klassert 898141ed9fSSteffen Klassert void ip4_datagram_release_cb(struct sock *sk) 908141ed9fSSteffen Klassert { 918141ed9fSSteffen Klassert const struct inet_sock *inet = inet_sk(sk); 928141ed9fSSteffen Klassert const struct ip_options_rcu *inet_opt; 938141ed9fSSteffen Klassert __be32 daddr = inet->inet_daddr; 948141ed9fSSteffen Klassert struct flowi4 fl4; 958141ed9fSSteffen Klassert struct rtable *rt; 968141ed9fSSteffen Klassert 978141ed9fSSteffen Klassert if (! __sk_dst_get(sk) || __sk_dst_check(sk, 0)) 988141ed9fSSteffen Klassert return; 998141ed9fSSteffen Klassert 1008141ed9fSSteffen Klassert rcu_read_lock(); 1018141ed9fSSteffen Klassert inet_opt = rcu_dereference(inet->inet_opt); 1028141ed9fSSteffen Klassert if (inet_opt && inet_opt->opt.srr) 1038141ed9fSSteffen Klassert daddr = inet_opt->opt.faddr; 1048141ed9fSSteffen Klassert rt = ip_route_output_ports(sock_net(sk), &fl4, sk, daddr, 1058141ed9fSSteffen Klassert inet->inet_saddr, inet->inet_dport, 1068141ed9fSSteffen Klassert inet->inet_sport, sk->sk_protocol, 1078141ed9fSSteffen Klassert RT_CONN_FLAGS(sk), sk->sk_bound_dev_if); 1088141ed9fSSteffen Klassert if (!IS_ERR(rt)) 1098141ed9fSSteffen Klassert __sk_dst_set(sk, &rt->dst); 1108141ed9fSSteffen Klassert rcu_read_unlock(); 1118141ed9fSSteffen Klassert } 1128141ed9fSSteffen Klassert EXPORT_SYMBOL_GPL(ip4_datagram_release_cb); 113