162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * net/dccp/ipv4.c 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * An implementation of the DCCP protocol 662306a36Sopenharmony_ci * Arnaldo Carvalho de Melo <acme@conectiva.com.br> 762306a36Sopenharmony_ci */ 862306a36Sopenharmony_ci 962306a36Sopenharmony_ci#include <linux/dccp.h> 1062306a36Sopenharmony_ci#include <linux/icmp.h> 1162306a36Sopenharmony_ci#include <linux/slab.h> 1262306a36Sopenharmony_ci#include <linux/module.h> 1362306a36Sopenharmony_ci#include <linux/skbuff.h> 1462306a36Sopenharmony_ci#include <linux/random.h> 1562306a36Sopenharmony_ci 1662306a36Sopenharmony_ci#include <net/icmp.h> 1762306a36Sopenharmony_ci#include <net/inet_common.h> 1862306a36Sopenharmony_ci#include <net/inet_hashtables.h> 1962306a36Sopenharmony_ci#include <net/inet_sock.h> 2062306a36Sopenharmony_ci#include <net/protocol.h> 2162306a36Sopenharmony_ci#include <net/sock.h> 2262306a36Sopenharmony_ci#include <net/timewait_sock.h> 2362306a36Sopenharmony_ci#include <net/tcp_states.h> 2462306a36Sopenharmony_ci#include <net/xfrm.h> 2562306a36Sopenharmony_ci#include <net/secure_seq.h> 2662306a36Sopenharmony_ci#include <net/netns/generic.h> 2762306a36Sopenharmony_ci 2862306a36Sopenharmony_ci#include "ackvec.h" 2962306a36Sopenharmony_ci#include "ccid.h" 3062306a36Sopenharmony_ci#include "dccp.h" 3162306a36Sopenharmony_ci#include "feat.h" 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_cistruct dccp_v4_pernet { 3462306a36Sopenharmony_ci struct sock *v4_ctl_sk; 3562306a36Sopenharmony_ci}; 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_cistatic unsigned int dccp_v4_pernet_id __read_mostly; 3862306a36Sopenharmony_ci 3962306a36Sopenharmony_ci/* 4062306a36Sopenharmony_ci * The per-net v4_ctl_sk socket is used for responding to 4162306a36Sopenharmony_ci * the Out-of-the-blue (OOTB) packets. A control sock will be created 4262306a36Sopenharmony_ci * for this socket at the initialization time. 4362306a36Sopenharmony_ci */ 4462306a36Sopenharmony_ci 4562306a36Sopenharmony_ciint dccp_v4_connect(struct sock *sk, struct sockaddr *uaddr, int addr_len) 4662306a36Sopenharmony_ci{ 4762306a36Sopenharmony_ci const struct sockaddr_in *usin = (struct sockaddr_in *)uaddr; 4862306a36Sopenharmony_ci struct inet_sock *inet = inet_sk(sk); 4962306a36Sopenharmony_ci struct dccp_sock *dp = dccp_sk(sk); 5062306a36Sopenharmony_ci __be16 orig_sport, orig_dport; 5162306a36Sopenharmony_ci __be32 daddr, nexthop; 5262306a36Sopenharmony_ci struct flowi4 *fl4; 5362306a36Sopenharmony_ci struct rtable *rt; 5462306a36Sopenharmony_ci int err; 5562306a36Sopenharmony_ci struct ip_options_rcu *inet_opt; 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_ci dp->dccps_role = DCCP_ROLE_CLIENT; 5862306a36Sopenharmony_ci 5962306a36Sopenharmony_ci if (addr_len < sizeof(struct sockaddr_in)) 6062306a36Sopenharmony_ci return -EINVAL; 6162306a36Sopenharmony_ci 6262306a36Sopenharmony_ci if (usin->sin_family != AF_INET) 6362306a36Sopenharmony_ci return -EAFNOSUPPORT; 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_ci nexthop = daddr = usin->sin_addr.s_addr; 6662306a36Sopenharmony_ci 6762306a36Sopenharmony_ci inet_opt = rcu_dereference_protected(inet->inet_opt, 6862306a36Sopenharmony_ci lockdep_sock_is_held(sk)); 6962306a36Sopenharmony_ci if (inet_opt != NULL && inet_opt->opt.srr) { 7062306a36Sopenharmony_ci if (daddr == 0) 7162306a36Sopenharmony_ci return -EINVAL; 7262306a36Sopenharmony_ci nexthop = inet_opt->opt.faddr; 7362306a36Sopenharmony_ci } 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_ci orig_sport = inet->inet_sport; 7662306a36Sopenharmony_ci orig_dport = usin->sin_port; 7762306a36Sopenharmony_ci fl4 = &inet->cork.fl.u.ip4; 7862306a36Sopenharmony_ci rt = ip_route_connect(fl4, nexthop, inet->inet_saddr, 7962306a36Sopenharmony_ci sk->sk_bound_dev_if, IPPROTO_DCCP, orig_sport, 8062306a36Sopenharmony_ci orig_dport, sk); 8162306a36Sopenharmony_ci if (IS_ERR(rt)) 8262306a36Sopenharmony_ci return PTR_ERR(rt); 8362306a36Sopenharmony_ci 8462306a36Sopenharmony_ci if (rt->rt_flags & (RTCF_MULTICAST | RTCF_BROADCAST)) { 8562306a36Sopenharmony_ci ip_rt_put(rt); 8662306a36Sopenharmony_ci return -ENETUNREACH; 8762306a36Sopenharmony_ci } 8862306a36Sopenharmony_ci 8962306a36Sopenharmony_ci if (inet_opt == NULL || !inet_opt->opt.srr) 9062306a36Sopenharmony_ci daddr = fl4->daddr; 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_ci if (inet->inet_saddr == 0) { 9362306a36Sopenharmony_ci err = inet_bhash2_update_saddr(sk, &fl4->saddr, AF_INET); 9462306a36Sopenharmony_ci if (err) { 9562306a36Sopenharmony_ci ip_rt_put(rt); 9662306a36Sopenharmony_ci return err; 9762306a36Sopenharmony_ci } 9862306a36Sopenharmony_ci } else { 9962306a36Sopenharmony_ci sk_rcv_saddr_set(sk, inet->inet_saddr); 10062306a36Sopenharmony_ci } 10162306a36Sopenharmony_ci 10262306a36Sopenharmony_ci inet->inet_dport = usin->sin_port; 10362306a36Sopenharmony_ci sk_daddr_set(sk, daddr); 10462306a36Sopenharmony_ci 10562306a36Sopenharmony_ci inet_csk(sk)->icsk_ext_hdr_len = 0; 10662306a36Sopenharmony_ci if (inet_opt) 10762306a36Sopenharmony_ci inet_csk(sk)->icsk_ext_hdr_len = inet_opt->opt.optlen; 10862306a36Sopenharmony_ci /* 10962306a36Sopenharmony_ci * Socket identity is still unknown (sport may be zero). 11062306a36Sopenharmony_ci * However we set state to DCCP_REQUESTING and not releasing socket 11162306a36Sopenharmony_ci * lock select source port, enter ourselves into the hash tables and 11262306a36Sopenharmony_ci * complete initialization after this. 11362306a36Sopenharmony_ci */ 11462306a36Sopenharmony_ci dccp_set_state(sk, DCCP_REQUESTING); 11562306a36Sopenharmony_ci err = inet_hash_connect(&dccp_death_row, sk); 11662306a36Sopenharmony_ci if (err != 0) 11762306a36Sopenharmony_ci goto failure; 11862306a36Sopenharmony_ci 11962306a36Sopenharmony_ci rt = ip_route_newports(fl4, rt, orig_sport, orig_dport, 12062306a36Sopenharmony_ci inet->inet_sport, inet->inet_dport, sk); 12162306a36Sopenharmony_ci if (IS_ERR(rt)) { 12262306a36Sopenharmony_ci err = PTR_ERR(rt); 12362306a36Sopenharmony_ci rt = NULL; 12462306a36Sopenharmony_ci goto failure; 12562306a36Sopenharmony_ci } 12662306a36Sopenharmony_ci /* OK, now commit destination to socket. */ 12762306a36Sopenharmony_ci sk_setup_caps(sk, &rt->dst); 12862306a36Sopenharmony_ci 12962306a36Sopenharmony_ci dp->dccps_iss = secure_dccp_sequence_number(inet->inet_saddr, 13062306a36Sopenharmony_ci inet->inet_daddr, 13162306a36Sopenharmony_ci inet->inet_sport, 13262306a36Sopenharmony_ci inet->inet_dport); 13362306a36Sopenharmony_ci atomic_set(&inet->inet_id, get_random_u16()); 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_ci err = dccp_connect(sk); 13662306a36Sopenharmony_ci rt = NULL; 13762306a36Sopenharmony_ci if (err != 0) 13862306a36Sopenharmony_ci goto failure; 13962306a36Sopenharmony_ciout: 14062306a36Sopenharmony_ci return err; 14162306a36Sopenharmony_cifailure: 14262306a36Sopenharmony_ci /* 14362306a36Sopenharmony_ci * This unhashes the socket and releases the local port, if necessary. 14462306a36Sopenharmony_ci */ 14562306a36Sopenharmony_ci dccp_set_state(sk, DCCP_CLOSED); 14662306a36Sopenharmony_ci inet_bhash2_reset_saddr(sk); 14762306a36Sopenharmony_ci ip_rt_put(rt); 14862306a36Sopenharmony_ci sk->sk_route_caps = 0; 14962306a36Sopenharmony_ci inet->inet_dport = 0; 15062306a36Sopenharmony_ci goto out; 15162306a36Sopenharmony_ci} 15262306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(dccp_v4_connect); 15362306a36Sopenharmony_ci 15462306a36Sopenharmony_ci/* 15562306a36Sopenharmony_ci * This routine does path mtu discovery as defined in RFC1191. 15662306a36Sopenharmony_ci */ 15762306a36Sopenharmony_cistatic inline void dccp_do_pmtu_discovery(struct sock *sk, 15862306a36Sopenharmony_ci const struct iphdr *iph, 15962306a36Sopenharmony_ci u32 mtu) 16062306a36Sopenharmony_ci{ 16162306a36Sopenharmony_ci struct dst_entry *dst; 16262306a36Sopenharmony_ci const struct inet_sock *inet = inet_sk(sk); 16362306a36Sopenharmony_ci const struct dccp_sock *dp = dccp_sk(sk); 16462306a36Sopenharmony_ci 16562306a36Sopenharmony_ci /* We are not interested in DCCP_LISTEN and request_socks (RESPONSEs 16662306a36Sopenharmony_ci * send out by Linux are always < 576bytes so they should go through 16762306a36Sopenharmony_ci * unfragmented). 16862306a36Sopenharmony_ci */ 16962306a36Sopenharmony_ci if (sk->sk_state == DCCP_LISTEN) 17062306a36Sopenharmony_ci return; 17162306a36Sopenharmony_ci 17262306a36Sopenharmony_ci dst = inet_csk_update_pmtu(sk, mtu); 17362306a36Sopenharmony_ci if (!dst) 17462306a36Sopenharmony_ci return; 17562306a36Sopenharmony_ci 17662306a36Sopenharmony_ci /* Something is about to be wrong... Remember soft error 17762306a36Sopenharmony_ci * for the case, if this connection will not able to recover. 17862306a36Sopenharmony_ci */ 17962306a36Sopenharmony_ci if (mtu < dst_mtu(dst) && ip_dont_fragment(sk, dst)) 18062306a36Sopenharmony_ci WRITE_ONCE(sk->sk_err_soft, EMSGSIZE); 18162306a36Sopenharmony_ci 18262306a36Sopenharmony_ci mtu = dst_mtu(dst); 18362306a36Sopenharmony_ci 18462306a36Sopenharmony_ci if (inet->pmtudisc != IP_PMTUDISC_DONT && 18562306a36Sopenharmony_ci ip_sk_accept_pmtu(sk) && 18662306a36Sopenharmony_ci inet_csk(sk)->icsk_pmtu_cookie > mtu) { 18762306a36Sopenharmony_ci dccp_sync_mss(sk, mtu); 18862306a36Sopenharmony_ci 18962306a36Sopenharmony_ci /* 19062306a36Sopenharmony_ci * From RFC 4340, sec. 14.1: 19162306a36Sopenharmony_ci * 19262306a36Sopenharmony_ci * DCCP-Sync packets are the best choice for upward 19362306a36Sopenharmony_ci * probing, since DCCP-Sync probes do not risk application 19462306a36Sopenharmony_ci * data loss. 19562306a36Sopenharmony_ci */ 19662306a36Sopenharmony_ci dccp_send_sync(sk, dp->dccps_gsr, DCCP_PKT_SYNC); 19762306a36Sopenharmony_ci } /* else let the usual retransmit timer handle it */ 19862306a36Sopenharmony_ci} 19962306a36Sopenharmony_ci 20062306a36Sopenharmony_cistatic void dccp_do_redirect(struct sk_buff *skb, struct sock *sk) 20162306a36Sopenharmony_ci{ 20262306a36Sopenharmony_ci struct dst_entry *dst = __sk_dst_check(sk, 0); 20362306a36Sopenharmony_ci 20462306a36Sopenharmony_ci if (dst) 20562306a36Sopenharmony_ci dst->ops->redirect(dst, sk, skb); 20662306a36Sopenharmony_ci} 20762306a36Sopenharmony_ci 20862306a36Sopenharmony_civoid dccp_req_err(struct sock *sk, u64 seq) 20962306a36Sopenharmony_ci { 21062306a36Sopenharmony_ci struct request_sock *req = inet_reqsk(sk); 21162306a36Sopenharmony_ci struct net *net = sock_net(sk); 21262306a36Sopenharmony_ci 21362306a36Sopenharmony_ci /* 21462306a36Sopenharmony_ci * ICMPs are not backlogged, hence we cannot get an established 21562306a36Sopenharmony_ci * socket here. 21662306a36Sopenharmony_ci */ 21762306a36Sopenharmony_ci if (!between48(seq, dccp_rsk(req)->dreq_iss, dccp_rsk(req)->dreq_gss)) { 21862306a36Sopenharmony_ci __NET_INC_STATS(net, LINUX_MIB_OUTOFWINDOWICMPS); 21962306a36Sopenharmony_ci } else { 22062306a36Sopenharmony_ci /* 22162306a36Sopenharmony_ci * Still in RESPOND, just remove it silently. 22262306a36Sopenharmony_ci * There is no good way to pass the error to the newly 22362306a36Sopenharmony_ci * created socket, and POSIX does not want network 22462306a36Sopenharmony_ci * errors returned from accept(). 22562306a36Sopenharmony_ci */ 22662306a36Sopenharmony_ci inet_csk_reqsk_queue_drop(req->rsk_listener, req); 22762306a36Sopenharmony_ci } 22862306a36Sopenharmony_ci reqsk_put(req); 22962306a36Sopenharmony_ci} 23062306a36Sopenharmony_ciEXPORT_SYMBOL(dccp_req_err); 23162306a36Sopenharmony_ci 23262306a36Sopenharmony_ci/* 23362306a36Sopenharmony_ci * This routine is called by the ICMP module when it gets some sort of error 23462306a36Sopenharmony_ci * condition. If err < 0 then the socket should be closed and the error 23562306a36Sopenharmony_ci * returned to the user. If err > 0 it's just the icmp type << 8 | icmp code. 23662306a36Sopenharmony_ci * After adjustment header points to the first 8 bytes of the tcp header. We 23762306a36Sopenharmony_ci * need to find the appropriate port. 23862306a36Sopenharmony_ci * 23962306a36Sopenharmony_ci * The locking strategy used here is very "optimistic". When someone else 24062306a36Sopenharmony_ci * accesses the socket the ICMP is just dropped and for some paths there is no 24162306a36Sopenharmony_ci * check at all. A more general error queue to queue errors for later handling 24262306a36Sopenharmony_ci * is probably better. 24362306a36Sopenharmony_ci */ 24462306a36Sopenharmony_cistatic int dccp_v4_err(struct sk_buff *skb, u32 info) 24562306a36Sopenharmony_ci{ 24662306a36Sopenharmony_ci const struct iphdr *iph = (struct iphdr *)skb->data; 24762306a36Sopenharmony_ci const u8 offset = iph->ihl << 2; 24862306a36Sopenharmony_ci const struct dccp_hdr *dh; 24962306a36Sopenharmony_ci struct dccp_sock *dp; 25062306a36Sopenharmony_ci const int type = icmp_hdr(skb)->type; 25162306a36Sopenharmony_ci const int code = icmp_hdr(skb)->code; 25262306a36Sopenharmony_ci struct sock *sk; 25362306a36Sopenharmony_ci __u64 seq; 25462306a36Sopenharmony_ci int err; 25562306a36Sopenharmony_ci struct net *net = dev_net(skb->dev); 25662306a36Sopenharmony_ci 25762306a36Sopenharmony_ci if (!pskb_may_pull(skb, offset + sizeof(*dh))) 25862306a36Sopenharmony_ci return -EINVAL; 25962306a36Sopenharmony_ci dh = (struct dccp_hdr *)(skb->data + offset); 26062306a36Sopenharmony_ci if (!pskb_may_pull(skb, offset + __dccp_basic_hdr_len(dh))) 26162306a36Sopenharmony_ci return -EINVAL; 26262306a36Sopenharmony_ci iph = (struct iphdr *)skb->data; 26362306a36Sopenharmony_ci dh = (struct dccp_hdr *)(skb->data + offset); 26462306a36Sopenharmony_ci 26562306a36Sopenharmony_ci sk = __inet_lookup_established(net, &dccp_hashinfo, 26662306a36Sopenharmony_ci iph->daddr, dh->dccph_dport, 26762306a36Sopenharmony_ci iph->saddr, ntohs(dh->dccph_sport), 26862306a36Sopenharmony_ci inet_iif(skb), 0); 26962306a36Sopenharmony_ci if (!sk) { 27062306a36Sopenharmony_ci __ICMP_INC_STATS(net, ICMP_MIB_INERRORS); 27162306a36Sopenharmony_ci return -ENOENT; 27262306a36Sopenharmony_ci } 27362306a36Sopenharmony_ci 27462306a36Sopenharmony_ci if (sk->sk_state == DCCP_TIME_WAIT) { 27562306a36Sopenharmony_ci inet_twsk_put(inet_twsk(sk)); 27662306a36Sopenharmony_ci return 0; 27762306a36Sopenharmony_ci } 27862306a36Sopenharmony_ci seq = dccp_hdr_seq(dh); 27962306a36Sopenharmony_ci if (sk->sk_state == DCCP_NEW_SYN_RECV) { 28062306a36Sopenharmony_ci dccp_req_err(sk, seq); 28162306a36Sopenharmony_ci return 0; 28262306a36Sopenharmony_ci } 28362306a36Sopenharmony_ci 28462306a36Sopenharmony_ci bh_lock_sock(sk); 28562306a36Sopenharmony_ci /* If too many ICMPs get dropped on busy 28662306a36Sopenharmony_ci * servers this needs to be solved differently. 28762306a36Sopenharmony_ci */ 28862306a36Sopenharmony_ci if (sock_owned_by_user(sk)) 28962306a36Sopenharmony_ci __NET_INC_STATS(net, LINUX_MIB_LOCKDROPPEDICMPS); 29062306a36Sopenharmony_ci 29162306a36Sopenharmony_ci if (sk->sk_state == DCCP_CLOSED) 29262306a36Sopenharmony_ci goto out; 29362306a36Sopenharmony_ci 29462306a36Sopenharmony_ci dp = dccp_sk(sk); 29562306a36Sopenharmony_ci if ((1 << sk->sk_state) & ~(DCCPF_REQUESTING | DCCPF_LISTEN) && 29662306a36Sopenharmony_ci !between48(seq, dp->dccps_awl, dp->dccps_awh)) { 29762306a36Sopenharmony_ci __NET_INC_STATS(net, LINUX_MIB_OUTOFWINDOWICMPS); 29862306a36Sopenharmony_ci goto out; 29962306a36Sopenharmony_ci } 30062306a36Sopenharmony_ci 30162306a36Sopenharmony_ci switch (type) { 30262306a36Sopenharmony_ci case ICMP_REDIRECT: 30362306a36Sopenharmony_ci if (!sock_owned_by_user(sk)) 30462306a36Sopenharmony_ci dccp_do_redirect(skb, sk); 30562306a36Sopenharmony_ci goto out; 30662306a36Sopenharmony_ci case ICMP_SOURCE_QUENCH: 30762306a36Sopenharmony_ci /* Just silently ignore these. */ 30862306a36Sopenharmony_ci goto out; 30962306a36Sopenharmony_ci case ICMP_PARAMETERPROB: 31062306a36Sopenharmony_ci err = EPROTO; 31162306a36Sopenharmony_ci break; 31262306a36Sopenharmony_ci case ICMP_DEST_UNREACH: 31362306a36Sopenharmony_ci if (code > NR_ICMP_UNREACH) 31462306a36Sopenharmony_ci goto out; 31562306a36Sopenharmony_ci 31662306a36Sopenharmony_ci if (code == ICMP_FRAG_NEEDED) { /* PMTU discovery (RFC1191) */ 31762306a36Sopenharmony_ci if (!sock_owned_by_user(sk)) 31862306a36Sopenharmony_ci dccp_do_pmtu_discovery(sk, iph, info); 31962306a36Sopenharmony_ci goto out; 32062306a36Sopenharmony_ci } 32162306a36Sopenharmony_ci 32262306a36Sopenharmony_ci err = icmp_err_convert[code].errno; 32362306a36Sopenharmony_ci break; 32462306a36Sopenharmony_ci case ICMP_TIME_EXCEEDED: 32562306a36Sopenharmony_ci err = EHOSTUNREACH; 32662306a36Sopenharmony_ci break; 32762306a36Sopenharmony_ci default: 32862306a36Sopenharmony_ci goto out; 32962306a36Sopenharmony_ci } 33062306a36Sopenharmony_ci 33162306a36Sopenharmony_ci switch (sk->sk_state) { 33262306a36Sopenharmony_ci case DCCP_REQUESTING: 33362306a36Sopenharmony_ci case DCCP_RESPOND: 33462306a36Sopenharmony_ci if (!sock_owned_by_user(sk)) { 33562306a36Sopenharmony_ci __DCCP_INC_STATS(DCCP_MIB_ATTEMPTFAILS); 33662306a36Sopenharmony_ci sk->sk_err = err; 33762306a36Sopenharmony_ci 33862306a36Sopenharmony_ci sk_error_report(sk); 33962306a36Sopenharmony_ci 34062306a36Sopenharmony_ci dccp_done(sk); 34162306a36Sopenharmony_ci } else { 34262306a36Sopenharmony_ci WRITE_ONCE(sk->sk_err_soft, err); 34362306a36Sopenharmony_ci } 34462306a36Sopenharmony_ci goto out; 34562306a36Sopenharmony_ci } 34662306a36Sopenharmony_ci 34762306a36Sopenharmony_ci /* If we've already connected we will keep trying 34862306a36Sopenharmony_ci * until we time out, or the user gives up. 34962306a36Sopenharmony_ci * 35062306a36Sopenharmony_ci * rfc1122 4.2.3.9 allows to consider as hard errors 35162306a36Sopenharmony_ci * only PROTO_UNREACH and PORT_UNREACH (well, FRAG_FAILED too, 35262306a36Sopenharmony_ci * but it is obsoleted by pmtu discovery). 35362306a36Sopenharmony_ci * 35462306a36Sopenharmony_ci * Note, that in modern internet, where routing is unreliable 35562306a36Sopenharmony_ci * and in each dark corner broken firewalls sit, sending random 35662306a36Sopenharmony_ci * errors ordered by their masters even this two messages finally lose 35762306a36Sopenharmony_ci * their original sense (even Linux sends invalid PORT_UNREACHs) 35862306a36Sopenharmony_ci * 35962306a36Sopenharmony_ci * Now we are in compliance with RFCs. 36062306a36Sopenharmony_ci * --ANK (980905) 36162306a36Sopenharmony_ci */ 36262306a36Sopenharmony_ci 36362306a36Sopenharmony_ci if (!sock_owned_by_user(sk) && inet_test_bit(RECVERR, sk)) { 36462306a36Sopenharmony_ci sk->sk_err = err; 36562306a36Sopenharmony_ci sk_error_report(sk); 36662306a36Sopenharmony_ci } else { /* Only an error on timeout */ 36762306a36Sopenharmony_ci WRITE_ONCE(sk->sk_err_soft, err); 36862306a36Sopenharmony_ci } 36962306a36Sopenharmony_ciout: 37062306a36Sopenharmony_ci bh_unlock_sock(sk); 37162306a36Sopenharmony_ci sock_put(sk); 37262306a36Sopenharmony_ci return 0; 37362306a36Sopenharmony_ci} 37462306a36Sopenharmony_ci 37562306a36Sopenharmony_cistatic inline __sum16 dccp_v4_csum_finish(struct sk_buff *skb, 37662306a36Sopenharmony_ci __be32 src, __be32 dst) 37762306a36Sopenharmony_ci{ 37862306a36Sopenharmony_ci return csum_tcpudp_magic(src, dst, skb->len, IPPROTO_DCCP, skb->csum); 37962306a36Sopenharmony_ci} 38062306a36Sopenharmony_ci 38162306a36Sopenharmony_civoid dccp_v4_send_check(struct sock *sk, struct sk_buff *skb) 38262306a36Sopenharmony_ci{ 38362306a36Sopenharmony_ci const struct inet_sock *inet = inet_sk(sk); 38462306a36Sopenharmony_ci struct dccp_hdr *dh = dccp_hdr(skb); 38562306a36Sopenharmony_ci 38662306a36Sopenharmony_ci dccp_csum_outgoing(skb); 38762306a36Sopenharmony_ci dh->dccph_checksum = dccp_v4_csum_finish(skb, 38862306a36Sopenharmony_ci inet->inet_saddr, 38962306a36Sopenharmony_ci inet->inet_daddr); 39062306a36Sopenharmony_ci} 39162306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(dccp_v4_send_check); 39262306a36Sopenharmony_ci 39362306a36Sopenharmony_cistatic inline u64 dccp_v4_init_sequence(const struct sk_buff *skb) 39462306a36Sopenharmony_ci{ 39562306a36Sopenharmony_ci return secure_dccp_sequence_number(ip_hdr(skb)->daddr, 39662306a36Sopenharmony_ci ip_hdr(skb)->saddr, 39762306a36Sopenharmony_ci dccp_hdr(skb)->dccph_dport, 39862306a36Sopenharmony_ci dccp_hdr(skb)->dccph_sport); 39962306a36Sopenharmony_ci} 40062306a36Sopenharmony_ci 40162306a36Sopenharmony_ci/* 40262306a36Sopenharmony_ci * The three way handshake has completed - we got a valid ACK or DATAACK - 40362306a36Sopenharmony_ci * now create the new socket. 40462306a36Sopenharmony_ci * 40562306a36Sopenharmony_ci * This is the equivalent of TCP's tcp_v4_syn_recv_sock 40662306a36Sopenharmony_ci */ 40762306a36Sopenharmony_cistruct sock *dccp_v4_request_recv_sock(const struct sock *sk, 40862306a36Sopenharmony_ci struct sk_buff *skb, 40962306a36Sopenharmony_ci struct request_sock *req, 41062306a36Sopenharmony_ci struct dst_entry *dst, 41162306a36Sopenharmony_ci struct request_sock *req_unhash, 41262306a36Sopenharmony_ci bool *own_req) 41362306a36Sopenharmony_ci{ 41462306a36Sopenharmony_ci struct inet_request_sock *ireq; 41562306a36Sopenharmony_ci struct inet_sock *newinet; 41662306a36Sopenharmony_ci struct sock *newsk; 41762306a36Sopenharmony_ci 41862306a36Sopenharmony_ci if (sk_acceptq_is_full(sk)) 41962306a36Sopenharmony_ci goto exit_overflow; 42062306a36Sopenharmony_ci 42162306a36Sopenharmony_ci newsk = dccp_create_openreq_child(sk, req, skb); 42262306a36Sopenharmony_ci if (newsk == NULL) 42362306a36Sopenharmony_ci goto exit_nonewsk; 42462306a36Sopenharmony_ci 42562306a36Sopenharmony_ci newinet = inet_sk(newsk); 42662306a36Sopenharmony_ci ireq = inet_rsk(req); 42762306a36Sopenharmony_ci sk_daddr_set(newsk, ireq->ir_rmt_addr); 42862306a36Sopenharmony_ci sk_rcv_saddr_set(newsk, ireq->ir_loc_addr); 42962306a36Sopenharmony_ci newinet->inet_saddr = ireq->ir_loc_addr; 43062306a36Sopenharmony_ci RCU_INIT_POINTER(newinet->inet_opt, rcu_dereference(ireq->ireq_opt)); 43162306a36Sopenharmony_ci newinet->mc_index = inet_iif(skb); 43262306a36Sopenharmony_ci newinet->mc_ttl = ip_hdr(skb)->ttl; 43362306a36Sopenharmony_ci atomic_set(&newinet->inet_id, get_random_u16()); 43462306a36Sopenharmony_ci 43562306a36Sopenharmony_ci if (dst == NULL && (dst = inet_csk_route_child_sock(sk, newsk, req)) == NULL) 43662306a36Sopenharmony_ci goto put_and_exit; 43762306a36Sopenharmony_ci 43862306a36Sopenharmony_ci sk_setup_caps(newsk, dst); 43962306a36Sopenharmony_ci 44062306a36Sopenharmony_ci dccp_sync_mss(newsk, dst_mtu(dst)); 44162306a36Sopenharmony_ci 44262306a36Sopenharmony_ci if (__inet_inherit_port(sk, newsk) < 0) 44362306a36Sopenharmony_ci goto put_and_exit; 44462306a36Sopenharmony_ci *own_req = inet_ehash_nolisten(newsk, req_to_sk(req_unhash), NULL); 44562306a36Sopenharmony_ci if (*own_req) 44662306a36Sopenharmony_ci ireq->ireq_opt = NULL; 44762306a36Sopenharmony_ci else 44862306a36Sopenharmony_ci newinet->inet_opt = NULL; 44962306a36Sopenharmony_ci return newsk; 45062306a36Sopenharmony_ci 45162306a36Sopenharmony_ciexit_overflow: 45262306a36Sopenharmony_ci __NET_INC_STATS(sock_net(sk), LINUX_MIB_LISTENOVERFLOWS); 45362306a36Sopenharmony_ciexit_nonewsk: 45462306a36Sopenharmony_ci dst_release(dst); 45562306a36Sopenharmony_ciexit: 45662306a36Sopenharmony_ci __NET_INC_STATS(sock_net(sk), LINUX_MIB_LISTENDROPS); 45762306a36Sopenharmony_ci return NULL; 45862306a36Sopenharmony_ciput_and_exit: 45962306a36Sopenharmony_ci newinet->inet_opt = NULL; 46062306a36Sopenharmony_ci inet_csk_prepare_forced_close(newsk); 46162306a36Sopenharmony_ci dccp_done(newsk); 46262306a36Sopenharmony_ci goto exit; 46362306a36Sopenharmony_ci} 46462306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(dccp_v4_request_recv_sock); 46562306a36Sopenharmony_ci 46662306a36Sopenharmony_cistatic struct dst_entry* dccp_v4_route_skb(struct net *net, struct sock *sk, 46762306a36Sopenharmony_ci struct sk_buff *skb) 46862306a36Sopenharmony_ci{ 46962306a36Sopenharmony_ci struct rtable *rt; 47062306a36Sopenharmony_ci const struct iphdr *iph = ip_hdr(skb); 47162306a36Sopenharmony_ci struct flowi4 fl4 = { 47262306a36Sopenharmony_ci .flowi4_oif = inet_iif(skb), 47362306a36Sopenharmony_ci .daddr = iph->saddr, 47462306a36Sopenharmony_ci .saddr = iph->daddr, 47562306a36Sopenharmony_ci .flowi4_tos = ip_sock_rt_tos(sk), 47662306a36Sopenharmony_ci .flowi4_scope = ip_sock_rt_scope(sk), 47762306a36Sopenharmony_ci .flowi4_proto = sk->sk_protocol, 47862306a36Sopenharmony_ci .fl4_sport = dccp_hdr(skb)->dccph_dport, 47962306a36Sopenharmony_ci .fl4_dport = dccp_hdr(skb)->dccph_sport, 48062306a36Sopenharmony_ci }; 48162306a36Sopenharmony_ci 48262306a36Sopenharmony_ci security_skb_classify_flow(skb, flowi4_to_flowi_common(&fl4)); 48362306a36Sopenharmony_ci rt = ip_route_output_flow(net, &fl4, sk); 48462306a36Sopenharmony_ci if (IS_ERR(rt)) { 48562306a36Sopenharmony_ci IP_INC_STATS(net, IPSTATS_MIB_OUTNOROUTES); 48662306a36Sopenharmony_ci return NULL; 48762306a36Sopenharmony_ci } 48862306a36Sopenharmony_ci 48962306a36Sopenharmony_ci return &rt->dst; 49062306a36Sopenharmony_ci} 49162306a36Sopenharmony_ci 49262306a36Sopenharmony_cistatic int dccp_v4_send_response(const struct sock *sk, struct request_sock *req) 49362306a36Sopenharmony_ci{ 49462306a36Sopenharmony_ci int err = -1; 49562306a36Sopenharmony_ci struct sk_buff *skb; 49662306a36Sopenharmony_ci struct dst_entry *dst; 49762306a36Sopenharmony_ci struct flowi4 fl4; 49862306a36Sopenharmony_ci 49962306a36Sopenharmony_ci dst = inet_csk_route_req(sk, &fl4, req); 50062306a36Sopenharmony_ci if (dst == NULL) 50162306a36Sopenharmony_ci goto out; 50262306a36Sopenharmony_ci 50362306a36Sopenharmony_ci skb = dccp_make_response(sk, dst, req); 50462306a36Sopenharmony_ci if (skb != NULL) { 50562306a36Sopenharmony_ci const struct inet_request_sock *ireq = inet_rsk(req); 50662306a36Sopenharmony_ci struct dccp_hdr *dh = dccp_hdr(skb); 50762306a36Sopenharmony_ci 50862306a36Sopenharmony_ci dh->dccph_checksum = dccp_v4_csum_finish(skb, ireq->ir_loc_addr, 50962306a36Sopenharmony_ci ireq->ir_rmt_addr); 51062306a36Sopenharmony_ci rcu_read_lock(); 51162306a36Sopenharmony_ci err = ip_build_and_send_pkt(skb, sk, ireq->ir_loc_addr, 51262306a36Sopenharmony_ci ireq->ir_rmt_addr, 51362306a36Sopenharmony_ci rcu_dereference(ireq->ireq_opt), 51462306a36Sopenharmony_ci inet_sk(sk)->tos); 51562306a36Sopenharmony_ci rcu_read_unlock(); 51662306a36Sopenharmony_ci err = net_xmit_eval(err); 51762306a36Sopenharmony_ci } 51862306a36Sopenharmony_ci 51962306a36Sopenharmony_ciout: 52062306a36Sopenharmony_ci dst_release(dst); 52162306a36Sopenharmony_ci return err; 52262306a36Sopenharmony_ci} 52362306a36Sopenharmony_ci 52462306a36Sopenharmony_cistatic void dccp_v4_ctl_send_reset(const struct sock *sk, struct sk_buff *rxskb) 52562306a36Sopenharmony_ci{ 52662306a36Sopenharmony_ci int err; 52762306a36Sopenharmony_ci const struct iphdr *rxiph; 52862306a36Sopenharmony_ci struct sk_buff *skb; 52962306a36Sopenharmony_ci struct dst_entry *dst; 53062306a36Sopenharmony_ci struct net *net = dev_net(skb_dst(rxskb)->dev); 53162306a36Sopenharmony_ci struct dccp_v4_pernet *pn; 53262306a36Sopenharmony_ci struct sock *ctl_sk; 53362306a36Sopenharmony_ci 53462306a36Sopenharmony_ci /* Never send a reset in response to a reset. */ 53562306a36Sopenharmony_ci if (dccp_hdr(rxskb)->dccph_type == DCCP_PKT_RESET) 53662306a36Sopenharmony_ci return; 53762306a36Sopenharmony_ci 53862306a36Sopenharmony_ci if (skb_rtable(rxskb)->rt_type != RTN_LOCAL) 53962306a36Sopenharmony_ci return; 54062306a36Sopenharmony_ci 54162306a36Sopenharmony_ci pn = net_generic(net, dccp_v4_pernet_id); 54262306a36Sopenharmony_ci ctl_sk = pn->v4_ctl_sk; 54362306a36Sopenharmony_ci dst = dccp_v4_route_skb(net, ctl_sk, rxskb); 54462306a36Sopenharmony_ci if (dst == NULL) 54562306a36Sopenharmony_ci return; 54662306a36Sopenharmony_ci 54762306a36Sopenharmony_ci skb = dccp_ctl_make_reset(ctl_sk, rxskb); 54862306a36Sopenharmony_ci if (skb == NULL) 54962306a36Sopenharmony_ci goto out; 55062306a36Sopenharmony_ci 55162306a36Sopenharmony_ci rxiph = ip_hdr(rxskb); 55262306a36Sopenharmony_ci dccp_hdr(skb)->dccph_checksum = dccp_v4_csum_finish(skb, rxiph->saddr, 55362306a36Sopenharmony_ci rxiph->daddr); 55462306a36Sopenharmony_ci skb_dst_set(skb, dst_clone(dst)); 55562306a36Sopenharmony_ci 55662306a36Sopenharmony_ci local_bh_disable(); 55762306a36Sopenharmony_ci bh_lock_sock(ctl_sk); 55862306a36Sopenharmony_ci err = ip_build_and_send_pkt(skb, ctl_sk, 55962306a36Sopenharmony_ci rxiph->daddr, rxiph->saddr, NULL, 56062306a36Sopenharmony_ci inet_sk(ctl_sk)->tos); 56162306a36Sopenharmony_ci bh_unlock_sock(ctl_sk); 56262306a36Sopenharmony_ci 56362306a36Sopenharmony_ci if (net_xmit_eval(err) == 0) { 56462306a36Sopenharmony_ci __DCCP_INC_STATS(DCCP_MIB_OUTSEGS); 56562306a36Sopenharmony_ci __DCCP_INC_STATS(DCCP_MIB_OUTRSTS); 56662306a36Sopenharmony_ci } 56762306a36Sopenharmony_ci local_bh_enable(); 56862306a36Sopenharmony_ciout: 56962306a36Sopenharmony_ci dst_release(dst); 57062306a36Sopenharmony_ci} 57162306a36Sopenharmony_ci 57262306a36Sopenharmony_cistatic void dccp_v4_reqsk_destructor(struct request_sock *req) 57362306a36Sopenharmony_ci{ 57462306a36Sopenharmony_ci dccp_feat_list_purge(&dccp_rsk(req)->dreq_featneg); 57562306a36Sopenharmony_ci kfree(rcu_dereference_protected(inet_rsk(req)->ireq_opt, 1)); 57662306a36Sopenharmony_ci} 57762306a36Sopenharmony_ci 57862306a36Sopenharmony_civoid dccp_syn_ack_timeout(const struct request_sock *req) 57962306a36Sopenharmony_ci{ 58062306a36Sopenharmony_ci} 58162306a36Sopenharmony_ciEXPORT_SYMBOL(dccp_syn_ack_timeout); 58262306a36Sopenharmony_ci 58362306a36Sopenharmony_cistatic struct request_sock_ops dccp_request_sock_ops __read_mostly = { 58462306a36Sopenharmony_ci .family = PF_INET, 58562306a36Sopenharmony_ci .obj_size = sizeof(struct dccp_request_sock), 58662306a36Sopenharmony_ci .rtx_syn_ack = dccp_v4_send_response, 58762306a36Sopenharmony_ci .send_ack = dccp_reqsk_send_ack, 58862306a36Sopenharmony_ci .destructor = dccp_v4_reqsk_destructor, 58962306a36Sopenharmony_ci .send_reset = dccp_v4_ctl_send_reset, 59062306a36Sopenharmony_ci .syn_ack_timeout = dccp_syn_ack_timeout, 59162306a36Sopenharmony_ci}; 59262306a36Sopenharmony_ci 59362306a36Sopenharmony_ciint dccp_v4_conn_request(struct sock *sk, struct sk_buff *skb) 59462306a36Sopenharmony_ci{ 59562306a36Sopenharmony_ci struct inet_request_sock *ireq; 59662306a36Sopenharmony_ci struct request_sock *req; 59762306a36Sopenharmony_ci struct dccp_request_sock *dreq; 59862306a36Sopenharmony_ci const __be32 service = dccp_hdr_request(skb)->dccph_req_service; 59962306a36Sopenharmony_ci struct dccp_skb_cb *dcb = DCCP_SKB_CB(skb); 60062306a36Sopenharmony_ci 60162306a36Sopenharmony_ci /* Never answer to DCCP_PKT_REQUESTs send to broadcast or multicast */ 60262306a36Sopenharmony_ci if (skb_rtable(skb)->rt_flags & (RTCF_BROADCAST | RTCF_MULTICAST)) 60362306a36Sopenharmony_ci return 0; /* discard, don't send a reset here */ 60462306a36Sopenharmony_ci 60562306a36Sopenharmony_ci if (dccp_bad_service_code(sk, service)) { 60662306a36Sopenharmony_ci dcb->dccpd_reset_code = DCCP_RESET_CODE_BAD_SERVICE_CODE; 60762306a36Sopenharmony_ci goto drop; 60862306a36Sopenharmony_ci } 60962306a36Sopenharmony_ci /* 61062306a36Sopenharmony_ci * TW buckets are converted to open requests without 61162306a36Sopenharmony_ci * limitations, they conserve resources and peer is 61262306a36Sopenharmony_ci * evidently real one. 61362306a36Sopenharmony_ci */ 61462306a36Sopenharmony_ci dcb->dccpd_reset_code = DCCP_RESET_CODE_TOO_BUSY; 61562306a36Sopenharmony_ci if (inet_csk_reqsk_queue_is_full(sk)) 61662306a36Sopenharmony_ci goto drop; 61762306a36Sopenharmony_ci 61862306a36Sopenharmony_ci if (sk_acceptq_is_full(sk)) 61962306a36Sopenharmony_ci goto drop; 62062306a36Sopenharmony_ci 62162306a36Sopenharmony_ci req = inet_reqsk_alloc(&dccp_request_sock_ops, sk, true); 62262306a36Sopenharmony_ci if (req == NULL) 62362306a36Sopenharmony_ci goto drop; 62462306a36Sopenharmony_ci 62562306a36Sopenharmony_ci if (dccp_reqsk_init(req, dccp_sk(sk), skb)) 62662306a36Sopenharmony_ci goto drop_and_free; 62762306a36Sopenharmony_ci 62862306a36Sopenharmony_ci dreq = dccp_rsk(req); 62962306a36Sopenharmony_ci if (dccp_parse_options(sk, dreq, skb)) 63062306a36Sopenharmony_ci goto drop_and_free; 63162306a36Sopenharmony_ci 63262306a36Sopenharmony_ci ireq = inet_rsk(req); 63362306a36Sopenharmony_ci sk_rcv_saddr_set(req_to_sk(req), ip_hdr(skb)->daddr); 63462306a36Sopenharmony_ci sk_daddr_set(req_to_sk(req), ip_hdr(skb)->saddr); 63562306a36Sopenharmony_ci ireq->ir_mark = inet_request_mark(sk, skb); 63662306a36Sopenharmony_ci ireq->ireq_family = AF_INET; 63762306a36Sopenharmony_ci ireq->ir_iif = READ_ONCE(sk->sk_bound_dev_if); 63862306a36Sopenharmony_ci 63962306a36Sopenharmony_ci if (security_inet_conn_request(sk, skb, req)) 64062306a36Sopenharmony_ci goto drop_and_free; 64162306a36Sopenharmony_ci 64262306a36Sopenharmony_ci /* 64362306a36Sopenharmony_ci * Step 3: Process LISTEN state 64462306a36Sopenharmony_ci * 64562306a36Sopenharmony_ci * Set S.ISR, S.GSR, S.SWL, S.SWH from packet or Init Cookie 64662306a36Sopenharmony_ci * 64762306a36Sopenharmony_ci * Setting S.SWL/S.SWH to is deferred to dccp_create_openreq_child(). 64862306a36Sopenharmony_ci */ 64962306a36Sopenharmony_ci dreq->dreq_isr = dcb->dccpd_seq; 65062306a36Sopenharmony_ci dreq->dreq_gsr = dreq->dreq_isr; 65162306a36Sopenharmony_ci dreq->dreq_iss = dccp_v4_init_sequence(skb); 65262306a36Sopenharmony_ci dreq->dreq_gss = dreq->dreq_iss; 65362306a36Sopenharmony_ci dreq->dreq_service = service; 65462306a36Sopenharmony_ci 65562306a36Sopenharmony_ci if (dccp_v4_send_response(sk, req)) 65662306a36Sopenharmony_ci goto drop_and_free; 65762306a36Sopenharmony_ci 65862306a36Sopenharmony_ci inet_csk_reqsk_queue_hash_add(sk, req, DCCP_TIMEOUT_INIT); 65962306a36Sopenharmony_ci reqsk_put(req); 66062306a36Sopenharmony_ci return 0; 66162306a36Sopenharmony_ci 66262306a36Sopenharmony_cidrop_and_free: 66362306a36Sopenharmony_ci reqsk_free(req); 66462306a36Sopenharmony_cidrop: 66562306a36Sopenharmony_ci __DCCP_INC_STATS(DCCP_MIB_ATTEMPTFAILS); 66662306a36Sopenharmony_ci return -1; 66762306a36Sopenharmony_ci} 66862306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(dccp_v4_conn_request); 66962306a36Sopenharmony_ci 67062306a36Sopenharmony_ciint dccp_v4_do_rcv(struct sock *sk, struct sk_buff *skb) 67162306a36Sopenharmony_ci{ 67262306a36Sopenharmony_ci struct dccp_hdr *dh = dccp_hdr(skb); 67362306a36Sopenharmony_ci 67462306a36Sopenharmony_ci if (sk->sk_state == DCCP_OPEN) { /* Fast path */ 67562306a36Sopenharmony_ci if (dccp_rcv_established(sk, skb, dh, skb->len)) 67662306a36Sopenharmony_ci goto reset; 67762306a36Sopenharmony_ci return 0; 67862306a36Sopenharmony_ci } 67962306a36Sopenharmony_ci 68062306a36Sopenharmony_ci /* 68162306a36Sopenharmony_ci * Step 3: Process LISTEN state 68262306a36Sopenharmony_ci * If P.type == Request or P contains a valid Init Cookie option, 68362306a36Sopenharmony_ci * (* Must scan the packet's options to check for Init 68462306a36Sopenharmony_ci * Cookies. Only Init Cookies are processed here, 68562306a36Sopenharmony_ci * however; other options are processed in Step 8. This 68662306a36Sopenharmony_ci * scan need only be performed if the endpoint uses Init 68762306a36Sopenharmony_ci * Cookies *) 68862306a36Sopenharmony_ci * (* Generate a new socket and switch to that socket *) 68962306a36Sopenharmony_ci * Set S := new socket for this port pair 69062306a36Sopenharmony_ci * S.state = RESPOND 69162306a36Sopenharmony_ci * Choose S.ISS (initial seqno) or set from Init Cookies 69262306a36Sopenharmony_ci * Initialize S.GAR := S.ISS 69362306a36Sopenharmony_ci * Set S.ISR, S.GSR, S.SWL, S.SWH from packet or Init Cookies 69462306a36Sopenharmony_ci * Continue with S.state == RESPOND 69562306a36Sopenharmony_ci * (* A Response packet will be generated in Step 11 *) 69662306a36Sopenharmony_ci * Otherwise, 69762306a36Sopenharmony_ci * Generate Reset(No Connection) unless P.type == Reset 69862306a36Sopenharmony_ci * Drop packet and return 69962306a36Sopenharmony_ci * 70062306a36Sopenharmony_ci * NOTE: the check for the packet types is done in 70162306a36Sopenharmony_ci * dccp_rcv_state_process 70262306a36Sopenharmony_ci */ 70362306a36Sopenharmony_ci 70462306a36Sopenharmony_ci if (dccp_rcv_state_process(sk, skb, dh, skb->len)) 70562306a36Sopenharmony_ci goto reset; 70662306a36Sopenharmony_ci return 0; 70762306a36Sopenharmony_ci 70862306a36Sopenharmony_cireset: 70962306a36Sopenharmony_ci dccp_v4_ctl_send_reset(sk, skb); 71062306a36Sopenharmony_ci kfree_skb(skb); 71162306a36Sopenharmony_ci return 0; 71262306a36Sopenharmony_ci} 71362306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(dccp_v4_do_rcv); 71462306a36Sopenharmony_ci 71562306a36Sopenharmony_ci/** 71662306a36Sopenharmony_ci * dccp_invalid_packet - check for malformed packets 71762306a36Sopenharmony_ci * @skb: Packet to validate 71862306a36Sopenharmony_ci * 71962306a36Sopenharmony_ci * Implements RFC 4340, 8.5: Step 1: Check header basics 72062306a36Sopenharmony_ci * Packets that fail these checks are ignored and do not receive Resets. 72162306a36Sopenharmony_ci */ 72262306a36Sopenharmony_ciint dccp_invalid_packet(struct sk_buff *skb) 72362306a36Sopenharmony_ci{ 72462306a36Sopenharmony_ci const struct dccp_hdr *dh; 72562306a36Sopenharmony_ci unsigned int cscov; 72662306a36Sopenharmony_ci u8 dccph_doff; 72762306a36Sopenharmony_ci 72862306a36Sopenharmony_ci if (skb->pkt_type != PACKET_HOST) 72962306a36Sopenharmony_ci return 1; 73062306a36Sopenharmony_ci 73162306a36Sopenharmony_ci /* If the packet is shorter than 12 bytes, drop packet and return */ 73262306a36Sopenharmony_ci if (!pskb_may_pull(skb, sizeof(struct dccp_hdr))) { 73362306a36Sopenharmony_ci DCCP_WARN("pskb_may_pull failed\n"); 73462306a36Sopenharmony_ci return 1; 73562306a36Sopenharmony_ci } 73662306a36Sopenharmony_ci 73762306a36Sopenharmony_ci dh = dccp_hdr(skb); 73862306a36Sopenharmony_ci 73962306a36Sopenharmony_ci /* If P.type is not understood, drop packet and return */ 74062306a36Sopenharmony_ci if (dh->dccph_type >= DCCP_PKT_INVALID) { 74162306a36Sopenharmony_ci DCCP_WARN("invalid packet type\n"); 74262306a36Sopenharmony_ci return 1; 74362306a36Sopenharmony_ci } 74462306a36Sopenharmony_ci 74562306a36Sopenharmony_ci /* 74662306a36Sopenharmony_ci * If P.Data Offset is too small for packet type, drop packet and return 74762306a36Sopenharmony_ci */ 74862306a36Sopenharmony_ci dccph_doff = dh->dccph_doff; 74962306a36Sopenharmony_ci if (dccph_doff < dccp_hdr_len(skb) / sizeof(u32)) { 75062306a36Sopenharmony_ci DCCP_WARN("P.Data Offset(%u) too small\n", dccph_doff); 75162306a36Sopenharmony_ci return 1; 75262306a36Sopenharmony_ci } 75362306a36Sopenharmony_ci /* 75462306a36Sopenharmony_ci * If P.Data Offset is too large for packet, drop packet and return 75562306a36Sopenharmony_ci */ 75662306a36Sopenharmony_ci if (!pskb_may_pull(skb, dccph_doff * sizeof(u32))) { 75762306a36Sopenharmony_ci DCCP_WARN("P.Data Offset(%u) too large\n", dccph_doff); 75862306a36Sopenharmony_ci return 1; 75962306a36Sopenharmony_ci } 76062306a36Sopenharmony_ci dh = dccp_hdr(skb); 76162306a36Sopenharmony_ci /* 76262306a36Sopenharmony_ci * If P.type is not Data, Ack, or DataAck and P.X == 0 (the packet 76362306a36Sopenharmony_ci * has short sequence numbers), drop packet and return 76462306a36Sopenharmony_ci */ 76562306a36Sopenharmony_ci if ((dh->dccph_type < DCCP_PKT_DATA || 76662306a36Sopenharmony_ci dh->dccph_type > DCCP_PKT_DATAACK) && dh->dccph_x == 0) { 76762306a36Sopenharmony_ci DCCP_WARN("P.type (%s) not Data || [Data]Ack, while P.X == 0\n", 76862306a36Sopenharmony_ci dccp_packet_name(dh->dccph_type)); 76962306a36Sopenharmony_ci return 1; 77062306a36Sopenharmony_ci } 77162306a36Sopenharmony_ci 77262306a36Sopenharmony_ci /* 77362306a36Sopenharmony_ci * If P.CsCov is too large for the packet size, drop packet and return. 77462306a36Sopenharmony_ci * This must come _before_ checksumming (not as RFC 4340 suggests). 77562306a36Sopenharmony_ci */ 77662306a36Sopenharmony_ci cscov = dccp_csum_coverage(skb); 77762306a36Sopenharmony_ci if (cscov > skb->len) { 77862306a36Sopenharmony_ci DCCP_WARN("P.CsCov %u exceeds packet length %d\n", 77962306a36Sopenharmony_ci dh->dccph_cscov, skb->len); 78062306a36Sopenharmony_ci return 1; 78162306a36Sopenharmony_ci } 78262306a36Sopenharmony_ci 78362306a36Sopenharmony_ci /* If header checksum is incorrect, drop packet and return. 78462306a36Sopenharmony_ci * (This step is completed in the AF-dependent functions.) */ 78562306a36Sopenharmony_ci skb->csum = skb_checksum(skb, 0, cscov, 0); 78662306a36Sopenharmony_ci 78762306a36Sopenharmony_ci return 0; 78862306a36Sopenharmony_ci} 78962306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(dccp_invalid_packet); 79062306a36Sopenharmony_ci 79162306a36Sopenharmony_ci/* this is called when real data arrives */ 79262306a36Sopenharmony_cistatic int dccp_v4_rcv(struct sk_buff *skb) 79362306a36Sopenharmony_ci{ 79462306a36Sopenharmony_ci const struct dccp_hdr *dh; 79562306a36Sopenharmony_ci const struct iphdr *iph; 79662306a36Sopenharmony_ci bool refcounted; 79762306a36Sopenharmony_ci struct sock *sk; 79862306a36Sopenharmony_ci int min_cov; 79962306a36Sopenharmony_ci 80062306a36Sopenharmony_ci /* Step 1: Check header basics */ 80162306a36Sopenharmony_ci 80262306a36Sopenharmony_ci if (dccp_invalid_packet(skb)) 80362306a36Sopenharmony_ci goto discard_it; 80462306a36Sopenharmony_ci 80562306a36Sopenharmony_ci iph = ip_hdr(skb); 80662306a36Sopenharmony_ci /* Step 1: If header checksum is incorrect, drop packet and return */ 80762306a36Sopenharmony_ci if (dccp_v4_csum_finish(skb, iph->saddr, iph->daddr)) { 80862306a36Sopenharmony_ci DCCP_WARN("dropped packet with invalid checksum\n"); 80962306a36Sopenharmony_ci goto discard_it; 81062306a36Sopenharmony_ci } 81162306a36Sopenharmony_ci 81262306a36Sopenharmony_ci dh = dccp_hdr(skb); 81362306a36Sopenharmony_ci 81462306a36Sopenharmony_ci DCCP_SKB_CB(skb)->dccpd_seq = dccp_hdr_seq(dh); 81562306a36Sopenharmony_ci DCCP_SKB_CB(skb)->dccpd_type = dh->dccph_type; 81662306a36Sopenharmony_ci 81762306a36Sopenharmony_ci dccp_pr_debug("%8.8s src=%pI4@%-5d dst=%pI4@%-5d seq=%llu", 81862306a36Sopenharmony_ci dccp_packet_name(dh->dccph_type), 81962306a36Sopenharmony_ci &iph->saddr, ntohs(dh->dccph_sport), 82062306a36Sopenharmony_ci &iph->daddr, ntohs(dh->dccph_dport), 82162306a36Sopenharmony_ci (unsigned long long) DCCP_SKB_CB(skb)->dccpd_seq); 82262306a36Sopenharmony_ci 82362306a36Sopenharmony_ci if (dccp_packet_without_ack(skb)) { 82462306a36Sopenharmony_ci DCCP_SKB_CB(skb)->dccpd_ack_seq = DCCP_PKT_WITHOUT_ACK_SEQ; 82562306a36Sopenharmony_ci dccp_pr_debug_cat("\n"); 82662306a36Sopenharmony_ci } else { 82762306a36Sopenharmony_ci DCCP_SKB_CB(skb)->dccpd_ack_seq = dccp_hdr_ack_seq(skb); 82862306a36Sopenharmony_ci dccp_pr_debug_cat(", ack=%llu\n", (unsigned long long) 82962306a36Sopenharmony_ci DCCP_SKB_CB(skb)->dccpd_ack_seq); 83062306a36Sopenharmony_ci } 83162306a36Sopenharmony_ci 83262306a36Sopenharmony_cilookup: 83362306a36Sopenharmony_ci sk = __inet_lookup_skb(&dccp_hashinfo, skb, __dccp_hdr_len(dh), 83462306a36Sopenharmony_ci dh->dccph_sport, dh->dccph_dport, 0, &refcounted); 83562306a36Sopenharmony_ci if (!sk) { 83662306a36Sopenharmony_ci dccp_pr_debug("failed to look up flow ID in table and " 83762306a36Sopenharmony_ci "get corresponding socket\n"); 83862306a36Sopenharmony_ci goto no_dccp_socket; 83962306a36Sopenharmony_ci } 84062306a36Sopenharmony_ci 84162306a36Sopenharmony_ci /* 84262306a36Sopenharmony_ci * Step 2: 84362306a36Sopenharmony_ci * ... or S.state == TIMEWAIT, 84462306a36Sopenharmony_ci * Generate Reset(No Connection) unless P.type == Reset 84562306a36Sopenharmony_ci * Drop packet and return 84662306a36Sopenharmony_ci */ 84762306a36Sopenharmony_ci if (sk->sk_state == DCCP_TIME_WAIT) { 84862306a36Sopenharmony_ci dccp_pr_debug("sk->sk_state == DCCP_TIME_WAIT: do_time_wait\n"); 84962306a36Sopenharmony_ci inet_twsk_put(inet_twsk(sk)); 85062306a36Sopenharmony_ci goto no_dccp_socket; 85162306a36Sopenharmony_ci } 85262306a36Sopenharmony_ci 85362306a36Sopenharmony_ci if (sk->sk_state == DCCP_NEW_SYN_RECV) { 85462306a36Sopenharmony_ci struct request_sock *req = inet_reqsk(sk); 85562306a36Sopenharmony_ci struct sock *nsk; 85662306a36Sopenharmony_ci 85762306a36Sopenharmony_ci sk = req->rsk_listener; 85862306a36Sopenharmony_ci if (unlikely(sk->sk_state != DCCP_LISTEN)) { 85962306a36Sopenharmony_ci inet_csk_reqsk_queue_drop_and_put(sk, req); 86062306a36Sopenharmony_ci goto lookup; 86162306a36Sopenharmony_ci } 86262306a36Sopenharmony_ci sock_hold(sk); 86362306a36Sopenharmony_ci refcounted = true; 86462306a36Sopenharmony_ci nsk = dccp_check_req(sk, skb, req); 86562306a36Sopenharmony_ci if (!nsk) { 86662306a36Sopenharmony_ci reqsk_put(req); 86762306a36Sopenharmony_ci goto discard_and_relse; 86862306a36Sopenharmony_ci } 86962306a36Sopenharmony_ci if (nsk == sk) { 87062306a36Sopenharmony_ci reqsk_put(req); 87162306a36Sopenharmony_ci } else if (dccp_child_process(sk, nsk, skb)) { 87262306a36Sopenharmony_ci dccp_v4_ctl_send_reset(sk, skb); 87362306a36Sopenharmony_ci goto discard_and_relse; 87462306a36Sopenharmony_ci } else { 87562306a36Sopenharmony_ci sock_put(sk); 87662306a36Sopenharmony_ci return 0; 87762306a36Sopenharmony_ci } 87862306a36Sopenharmony_ci } 87962306a36Sopenharmony_ci /* 88062306a36Sopenharmony_ci * RFC 4340, sec. 9.2.1: Minimum Checksum Coverage 88162306a36Sopenharmony_ci * o if MinCsCov = 0, only packets with CsCov = 0 are accepted 88262306a36Sopenharmony_ci * o if MinCsCov > 0, also accept packets with CsCov >= MinCsCov 88362306a36Sopenharmony_ci */ 88462306a36Sopenharmony_ci min_cov = dccp_sk(sk)->dccps_pcrlen; 88562306a36Sopenharmony_ci if (dh->dccph_cscov && (min_cov == 0 || dh->dccph_cscov < min_cov)) { 88662306a36Sopenharmony_ci dccp_pr_debug("Packet CsCov %d does not satisfy MinCsCov %d\n", 88762306a36Sopenharmony_ci dh->dccph_cscov, min_cov); 88862306a36Sopenharmony_ci /* FIXME: "Such packets SHOULD be reported using Data Dropped 88962306a36Sopenharmony_ci * options (Section 11.7) with Drop Code 0, Protocol 89062306a36Sopenharmony_ci * Constraints." */ 89162306a36Sopenharmony_ci goto discard_and_relse; 89262306a36Sopenharmony_ci } 89362306a36Sopenharmony_ci 89462306a36Sopenharmony_ci if (!xfrm4_policy_check(sk, XFRM_POLICY_IN, skb)) 89562306a36Sopenharmony_ci goto discard_and_relse; 89662306a36Sopenharmony_ci nf_reset_ct(skb); 89762306a36Sopenharmony_ci 89862306a36Sopenharmony_ci return __sk_receive_skb(sk, skb, 1, dh->dccph_doff * 4, refcounted); 89962306a36Sopenharmony_ci 90062306a36Sopenharmony_cino_dccp_socket: 90162306a36Sopenharmony_ci if (!xfrm4_policy_check(NULL, XFRM_POLICY_IN, skb)) 90262306a36Sopenharmony_ci goto discard_it; 90362306a36Sopenharmony_ci /* 90462306a36Sopenharmony_ci * Step 2: 90562306a36Sopenharmony_ci * If no socket ... 90662306a36Sopenharmony_ci * Generate Reset(No Connection) unless P.type == Reset 90762306a36Sopenharmony_ci * Drop packet and return 90862306a36Sopenharmony_ci */ 90962306a36Sopenharmony_ci if (dh->dccph_type != DCCP_PKT_RESET) { 91062306a36Sopenharmony_ci DCCP_SKB_CB(skb)->dccpd_reset_code = 91162306a36Sopenharmony_ci DCCP_RESET_CODE_NO_CONNECTION; 91262306a36Sopenharmony_ci dccp_v4_ctl_send_reset(sk, skb); 91362306a36Sopenharmony_ci } 91462306a36Sopenharmony_ci 91562306a36Sopenharmony_cidiscard_it: 91662306a36Sopenharmony_ci kfree_skb(skb); 91762306a36Sopenharmony_ci return 0; 91862306a36Sopenharmony_ci 91962306a36Sopenharmony_cidiscard_and_relse: 92062306a36Sopenharmony_ci if (refcounted) 92162306a36Sopenharmony_ci sock_put(sk); 92262306a36Sopenharmony_ci goto discard_it; 92362306a36Sopenharmony_ci} 92462306a36Sopenharmony_ci 92562306a36Sopenharmony_cistatic const struct inet_connection_sock_af_ops dccp_ipv4_af_ops = { 92662306a36Sopenharmony_ci .queue_xmit = ip_queue_xmit, 92762306a36Sopenharmony_ci .send_check = dccp_v4_send_check, 92862306a36Sopenharmony_ci .rebuild_header = inet_sk_rebuild_header, 92962306a36Sopenharmony_ci .conn_request = dccp_v4_conn_request, 93062306a36Sopenharmony_ci .syn_recv_sock = dccp_v4_request_recv_sock, 93162306a36Sopenharmony_ci .net_header_len = sizeof(struct iphdr), 93262306a36Sopenharmony_ci .setsockopt = ip_setsockopt, 93362306a36Sopenharmony_ci .getsockopt = ip_getsockopt, 93462306a36Sopenharmony_ci .addr2sockaddr = inet_csk_addr2sockaddr, 93562306a36Sopenharmony_ci .sockaddr_len = sizeof(struct sockaddr_in), 93662306a36Sopenharmony_ci}; 93762306a36Sopenharmony_ci 93862306a36Sopenharmony_cistatic int dccp_v4_init_sock(struct sock *sk) 93962306a36Sopenharmony_ci{ 94062306a36Sopenharmony_ci static __u8 dccp_v4_ctl_sock_initialized; 94162306a36Sopenharmony_ci int err = dccp_init_sock(sk, dccp_v4_ctl_sock_initialized); 94262306a36Sopenharmony_ci 94362306a36Sopenharmony_ci if (err == 0) { 94462306a36Sopenharmony_ci if (unlikely(!dccp_v4_ctl_sock_initialized)) 94562306a36Sopenharmony_ci dccp_v4_ctl_sock_initialized = 1; 94662306a36Sopenharmony_ci inet_csk(sk)->icsk_af_ops = &dccp_ipv4_af_ops; 94762306a36Sopenharmony_ci } 94862306a36Sopenharmony_ci 94962306a36Sopenharmony_ci return err; 95062306a36Sopenharmony_ci} 95162306a36Sopenharmony_ci 95262306a36Sopenharmony_cistatic struct timewait_sock_ops dccp_timewait_sock_ops = { 95362306a36Sopenharmony_ci .twsk_obj_size = sizeof(struct inet_timewait_sock), 95462306a36Sopenharmony_ci}; 95562306a36Sopenharmony_ci 95662306a36Sopenharmony_cistatic struct proto dccp_v4_prot = { 95762306a36Sopenharmony_ci .name = "DCCP", 95862306a36Sopenharmony_ci .owner = THIS_MODULE, 95962306a36Sopenharmony_ci .close = dccp_close, 96062306a36Sopenharmony_ci .connect = dccp_v4_connect, 96162306a36Sopenharmony_ci .disconnect = dccp_disconnect, 96262306a36Sopenharmony_ci .ioctl = dccp_ioctl, 96362306a36Sopenharmony_ci .init = dccp_v4_init_sock, 96462306a36Sopenharmony_ci .setsockopt = dccp_setsockopt, 96562306a36Sopenharmony_ci .getsockopt = dccp_getsockopt, 96662306a36Sopenharmony_ci .sendmsg = dccp_sendmsg, 96762306a36Sopenharmony_ci .recvmsg = dccp_recvmsg, 96862306a36Sopenharmony_ci .backlog_rcv = dccp_v4_do_rcv, 96962306a36Sopenharmony_ci .hash = inet_hash, 97062306a36Sopenharmony_ci .unhash = inet_unhash, 97162306a36Sopenharmony_ci .accept = inet_csk_accept, 97262306a36Sopenharmony_ci .get_port = inet_csk_get_port, 97362306a36Sopenharmony_ci .shutdown = dccp_shutdown, 97462306a36Sopenharmony_ci .destroy = dccp_destroy_sock, 97562306a36Sopenharmony_ci .orphan_count = &dccp_orphan_count, 97662306a36Sopenharmony_ci .max_header = MAX_DCCP_HEADER, 97762306a36Sopenharmony_ci .obj_size = sizeof(struct dccp_sock), 97862306a36Sopenharmony_ci .slab_flags = SLAB_TYPESAFE_BY_RCU, 97962306a36Sopenharmony_ci .rsk_prot = &dccp_request_sock_ops, 98062306a36Sopenharmony_ci .twsk_prot = &dccp_timewait_sock_ops, 98162306a36Sopenharmony_ci .h.hashinfo = &dccp_hashinfo, 98262306a36Sopenharmony_ci}; 98362306a36Sopenharmony_ci 98462306a36Sopenharmony_cistatic const struct net_protocol dccp_v4_protocol = { 98562306a36Sopenharmony_ci .handler = dccp_v4_rcv, 98662306a36Sopenharmony_ci .err_handler = dccp_v4_err, 98762306a36Sopenharmony_ci .no_policy = 1, 98862306a36Sopenharmony_ci .icmp_strict_tag_validation = 1, 98962306a36Sopenharmony_ci}; 99062306a36Sopenharmony_ci 99162306a36Sopenharmony_cistatic const struct proto_ops inet_dccp_ops = { 99262306a36Sopenharmony_ci .family = PF_INET, 99362306a36Sopenharmony_ci .owner = THIS_MODULE, 99462306a36Sopenharmony_ci .release = inet_release, 99562306a36Sopenharmony_ci .bind = inet_bind, 99662306a36Sopenharmony_ci .connect = inet_stream_connect, 99762306a36Sopenharmony_ci .socketpair = sock_no_socketpair, 99862306a36Sopenharmony_ci .accept = inet_accept, 99962306a36Sopenharmony_ci .getname = inet_getname, 100062306a36Sopenharmony_ci /* FIXME: work on tcp_poll to rename it to inet_csk_poll */ 100162306a36Sopenharmony_ci .poll = dccp_poll, 100262306a36Sopenharmony_ci .ioctl = inet_ioctl, 100362306a36Sopenharmony_ci .gettstamp = sock_gettstamp, 100462306a36Sopenharmony_ci /* FIXME: work on inet_listen to rename it to sock_common_listen */ 100562306a36Sopenharmony_ci .listen = inet_dccp_listen, 100662306a36Sopenharmony_ci .shutdown = inet_shutdown, 100762306a36Sopenharmony_ci .setsockopt = sock_common_setsockopt, 100862306a36Sopenharmony_ci .getsockopt = sock_common_getsockopt, 100962306a36Sopenharmony_ci .sendmsg = inet_sendmsg, 101062306a36Sopenharmony_ci .recvmsg = sock_common_recvmsg, 101162306a36Sopenharmony_ci .mmap = sock_no_mmap, 101262306a36Sopenharmony_ci}; 101362306a36Sopenharmony_ci 101462306a36Sopenharmony_cistatic struct inet_protosw dccp_v4_protosw = { 101562306a36Sopenharmony_ci .type = SOCK_DCCP, 101662306a36Sopenharmony_ci .protocol = IPPROTO_DCCP, 101762306a36Sopenharmony_ci .prot = &dccp_v4_prot, 101862306a36Sopenharmony_ci .ops = &inet_dccp_ops, 101962306a36Sopenharmony_ci .flags = INET_PROTOSW_ICSK, 102062306a36Sopenharmony_ci}; 102162306a36Sopenharmony_ci 102262306a36Sopenharmony_cistatic int __net_init dccp_v4_init_net(struct net *net) 102362306a36Sopenharmony_ci{ 102462306a36Sopenharmony_ci struct dccp_v4_pernet *pn = net_generic(net, dccp_v4_pernet_id); 102562306a36Sopenharmony_ci 102662306a36Sopenharmony_ci if (dccp_hashinfo.bhash == NULL) 102762306a36Sopenharmony_ci return -ESOCKTNOSUPPORT; 102862306a36Sopenharmony_ci 102962306a36Sopenharmony_ci return inet_ctl_sock_create(&pn->v4_ctl_sk, PF_INET, 103062306a36Sopenharmony_ci SOCK_DCCP, IPPROTO_DCCP, net); 103162306a36Sopenharmony_ci} 103262306a36Sopenharmony_ci 103362306a36Sopenharmony_cistatic void __net_exit dccp_v4_exit_net(struct net *net) 103462306a36Sopenharmony_ci{ 103562306a36Sopenharmony_ci struct dccp_v4_pernet *pn = net_generic(net, dccp_v4_pernet_id); 103662306a36Sopenharmony_ci 103762306a36Sopenharmony_ci inet_ctl_sock_destroy(pn->v4_ctl_sk); 103862306a36Sopenharmony_ci} 103962306a36Sopenharmony_ci 104062306a36Sopenharmony_cistatic void __net_exit dccp_v4_exit_batch(struct list_head *net_exit_list) 104162306a36Sopenharmony_ci{ 104262306a36Sopenharmony_ci inet_twsk_purge(&dccp_hashinfo, AF_INET); 104362306a36Sopenharmony_ci} 104462306a36Sopenharmony_ci 104562306a36Sopenharmony_cistatic struct pernet_operations dccp_v4_ops = { 104662306a36Sopenharmony_ci .init = dccp_v4_init_net, 104762306a36Sopenharmony_ci .exit = dccp_v4_exit_net, 104862306a36Sopenharmony_ci .exit_batch = dccp_v4_exit_batch, 104962306a36Sopenharmony_ci .id = &dccp_v4_pernet_id, 105062306a36Sopenharmony_ci .size = sizeof(struct dccp_v4_pernet), 105162306a36Sopenharmony_ci}; 105262306a36Sopenharmony_ci 105362306a36Sopenharmony_cistatic int __init dccp_v4_init(void) 105462306a36Sopenharmony_ci{ 105562306a36Sopenharmony_ci int err = proto_register(&dccp_v4_prot, 1); 105662306a36Sopenharmony_ci 105762306a36Sopenharmony_ci if (err) 105862306a36Sopenharmony_ci goto out; 105962306a36Sopenharmony_ci 106062306a36Sopenharmony_ci inet_register_protosw(&dccp_v4_protosw); 106162306a36Sopenharmony_ci 106262306a36Sopenharmony_ci err = register_pernet_subsys(&dccp_v4_ops); 106362306a36Sopenharmony_ci if (err) 106462306a36Sopenharmony_ci goto out_destroy_ctl_sock; 106562306a36Sopenharmony_ci 106662306a36Sopenharmony_ci err = inet_add_protocol(&dccp_v4_protocol, IPPROTO_DCCP); 106762306a36Sopenharmony_ci if (err) 106862306a36Sopenharmony_ci goto out_proto_unregister; 106962306a36Sopenharmony_ci 107062306a36Sopenharmony_ciout: 107162306a36Sopenharmony_ci return err; 107262306a36Sopenharmony_ciout_proto_unregister: 107362306a36Sopenharmony_ci unregister_pernet_subsys(&dccp_v4_ops); 107462306a36Sopenharmony_ciout_destroy_ctl_sock: 107562306a36Sopenharmony_ci inet_unregister_protosw(&dccp_v4_protosw); 107662306a36Sopenharmony_ci proto_unregister(&dccp_v4_prot); 107762306a36Sopenharmony_ci goto out; 107862306a36Sopenharmony_ci} 107962306a36Sopenharmony_ci 108062306a36Sopenharmony_cistatic void __exit dccp_v4_exit(void) 108162306a36Sopenharmony_ci{ 108262306a36Sopenharmony_ci inet_del_protocol(&dccp_v4_protocol, IPPROTO_DCCP); 108362306a36Sopenharmony_ci unregister_pernet_subsys(&dccp_v4_ops); 108462306a36Sopenharmony_ci inet_unregister_protosw(&dccp_v4_protosw); 108562306a36Sopenharmony_ci proto_unregister(&dccp_v4_prot); 108662306a36Sopenharmony_ci} 108762306a36Sopenharmony_ci 108862306a36Sopenharmony_cimodule_init(dccp_v4_init); 108962306a36Sopenharmony_cimodule_exit(dccp_v4_exit); 109062306a36Sopenharmony_ci 109162306a36Sopenharmony_ci/* 109262306a36Sopenharmony_ci * __stringify doesn't likes enums, so use SOCK_DCCP (6) and IPPROTO_DCCP (33) 109362306a36Sopenharmony_ci * values directly, Also cover the case where the protocol is not specified, 109462306a36Sopenharmony_ci * i.e. net-pf-PF_INET-proto-0-type-SOCK_DCCP 109562306a36Sopenharmony_ci */ 109662306a36Sopenharmony_ciMODULE_ALIAS_NET_PF_PROTO_TYPE(PF_INET, 33, 6); 109762306a36Sopenharmony_ciMODULE_ALIAS_NET_PF_PROTO_TYPE(PF_INET, 0, 6); 109862306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 109962306a36Sopenharmony_ciMODULE_AUTHOR("Arnaldo Carvalho de Melo <acme@mandriva.com>"); 110062306a36Sopenharmony_ciMODULE_DESCRIPTION("DCCP - Datagram Congestion Controlled Protocol"); 1101