162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * DCCP over IPv6 462306a36Sopenharmony_ci * Linux INET6 implementation 562306a36Sopenharmony_ci * 662306a36Sopenharmony_ci * Based on net/dccp6/ipv6.c 762306a36Sopenharmony_ci * 862306a36Sopenharmony_ci * Arnaldo Carvalho de Melo <acme@ghostprotocols.net> 962306a36Sopenharmony_ci */ 1062306a36Sopenharmony_ci 1162306a36Sopenharmony_ci#include <linux/module.h> 1262306a36Sopenharmony_ci#include <linux/random.h> 1362306a36Sopenharmony_ci#include <linux/slab.h> 1462306a36Sopenharmony_ci#include <linux/xfrm.h> 1562306a36Sopenharmony_ci#include <linux/string.h> 1662306a36Sopenharmony_ci 1762306a36Sopenharmony_ci#include <net/addrconf.h> 1862306a36Sopenharmony_ci#include <net/inet_common.h> 1962306a36Sopenharmony_ci#include <net/inet_hashtables.h> 2062306a36Sopenharmony_ci#include <net/inet_sock.h> 2162306a36Sopenharmony_ci#include <net/inet6_connection_sock.h> 2262306a36Sopenharmony_ci#include <net/inet6_hashtables.h> 2362306a36Sopenharmony_ci#include <net/ip6_route.h> 2462306a36Sopenharmony_ci#include <net/ipv6.h> 2562306a36Sopenharmony_ci#include <net/protocol.h> 2662306a36Sopenharmony_ci#include <net/transp_v6.h> 2762306a36Sopenharmony_ci#include <net/ip6_checksum.h> 2862306a36Sopenharmony_ci#include <net/xfrm.h> 2962306a36Sopenharmony_ci#include <net/secure_seq.h> 3062306a36Sopenharmony_ci#include <net/netns/generic.h> 3162306a36Sopenharmony_ci#include <net/sock.h> 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_ci#include "dccp.h" 3462306a36Sopenharmony_ci#include "ipv6.h" 3562306a36Sopenharmony_ci#include "feat.h" 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_cistruct dccp_v6_pernet { 3862306a36Sopenharmony_ci struct sock *v6_ctl_sk; 3962306a36Sopenharmony_ci}; 4062306a36Sopenharmony_ci 4162306a36Sopenharmony_cistatic unsigned int dccp_v6_pernet_id __read_mostly; 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_ci/* The per-net v6_ctl_sk is used for sending RSTs and ACKs */ 4462306a36Sopenharmony_ci 4562306a36Sopenharmony_cistatic const struct inet_connection_sock_af_ops dccp_ipv6_mapped; 4662306a36Sopenharmony_cistatic const struct inet_connection_sock_af_ops dccp_ipv6_af_ops; 4762306a36Sopenharmony_ci 4862306a36Sopenharmony_ci/* add pseudo-header to DCCP checksum stored in skb->csum */ 4962306a36Sopenharmony_cistatic inline __sum16 dccp_v6_csum_finish(struct sk_buff *skb, 5062306a36Sopenharmony_ci const struct in6_addr *saddr, 5162306a36Sopenharmony_ci const struct in6_addr *daddr) 5262306a36Sopenharmony_ci{ 5362306a36Sopenharmony_ci return csum_ipv6_magic(saddr, daddr, skb->len, IPPROTO_DCCP, skb->csum); 5462306a36Sopenharmony_ci} 5562306a36Sopenharmony_ci 5662306a36Sopenharmony_cistatic inline void dccp_v6_send_check(struct sock *sk, struct sk_buff *skb) 5762306a36Sopenharmony_ci{ 5862306a36Sopenharmony_ci struct ipv6_pinfo *np = inet6_sk(sk); 5962306a36Sopenharmony_ci struct dccp_hdr *dh = dccp_hdr(skb); 6062306a36Sopenharmony_ci 6162306a36Sopenharmony_ci dccp_csum_outgoing(skb); 6262306a36Sopenharmony_ci dh->dccph_checksum = dccp_v6_csum_finish(skb, &np->saddr, &sk->sk_v6_daddr); 6362306a36Sopenharmony_ci} 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_cistatic inline __u64 dccp_v6_init_sequence(struct sk_buff *skb) 6662306a36Sopenharmony_ci{ 6762306a36Sopenharmony_ci return secure_dccpv6_sequence_number(ipv6_hdr(skb)->daddr.s6_addr32, 6862306a36Sopenharmony_ci ipv6_hdr(skb)->saddr.s6_addr32, 6962306a36Sopenharmony_ci dccp_hdr(skb)->dccph_dport, 7062306a36Sopenharmony_ci dccp_hdr(skb)->dccph_sport ); 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_ci} 7362306a36Sopenharmony_ci 7462306a36Sopenharmony_cistatic int dccp_v6_err(struct sk_buff *skb, struct inet6_skb_parm *opt, 7562306a36Sopenharmony_ci u8 type, u8 code, int offset, __be32 info) 7662306a36Sopenharmony_ci{ 7762306a36Sopenharmony_ci const struct ipv6hdr *hdr; 7862306a36Sopenharmony_ci const struct dccp_hdr *dh; 7962306a36Sopenharmony_ci struct dccp_sock *dp; 8062306a36Sopenharmony_ci struct ipv6_pinfo *np; 8162306a36Sopenharmony_ci struct sock *sk; 8262306a36Sopenharmony_ci int err; 8362306a36Sopenharmony_ci __u64 seq; 8462306a36Sopenharmony_ci struct net *net = dev_net(skb->dev); 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_ci if (!pskb_may_pull(skb, offset + sizeof(*dh))) 8762306a36Sopenharmony_ci return -EINVAL; 8862306a36Sopenharmony_ci dh = (struct dccp_hdr *)(skb->data + offset); 8962306a36Sopenharmony_ci if (!pskb_may_pull(skb, offset + __dccp_basic_hdr_len(dh))) 9062306a36Sopenharmony_ci return -EINVAL; 9162306a36Sopenharmony_ci hdr = (const struct ipv6hdr *)skb->data; 9262306a36Sopenharmony_ci dh = (struct dccp_hdr *)(skb->data + offset); 9362306a36Sopenharmony_ci 9462306a36Sopenharmony_ci sk = __inet6_lookup_established(net, &dccp_hashinfo, 9562306a36Sopenharmony_ci &hdr->daddr, dh->dccph_dport, 9662306a36Sopenharmony_ci &hdr->saddr, ntohs(dh->dccph_sport), 9762306a36Sopenharmony_ci inet6_iif(skb), 0); 9862306a36Sopenharmony_ci 9962306a36Sopenharmony_ci if (!sk) { 10062306a36Sopenharmony_ci __ICMP6_INC_STATS(net, __in6_dev_get(skb->dev), 10162306a36Sopenharmony_ci ICMP6_MIB_INERRORS); 10262306a36Sopenharmony_ci return -ENOENT; 10362306a36Sopenharmony_ci } 10462306a36Sopenharmony_ci 10562306a36Sopenharmony_ci if (sk->sk_state == DCCP_TIME_WAIT) { 10662306a36Sopenharmony_ci inet_twsk_put(inet_twsk(sk)); 10762306a36Sopenharmony_ci return 0; 10862306a36Sopenharmony_ci } 10962306a36Sopenharmony_ci seq = dccp_hdr_seq(dh); 11062306a36Sopenharmony_ci if (sk->sk_state == DCCP_NEW_SYN_RECV) { 11162306a36Sopenharmony_ci dccp_req_err(sk, seq); 11262306a36Sopenharmony_ci return 0; 11362306a36Sopenharmony_ci } 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_ci bh_lock_sock(sk); 11662306a36Sopenharmony_ci if (sock_owned_by_user(sk)) 11762306a36Sopenharmony_ci __NET_INC_STATS(net, LINUX_MIB_LOCKDROPPEDICMPS); 11862306a36Sopenharmony_ci 11962306a36Sopenharmony_ci if (sk->sk_state == DCCP_CLOSED) 12062306a36Sopenharmony_ci goto out; 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_ci dp = dccp_sk(sk); 12362306a36Sopenharmony_ci if ((1 << sk->sk_state) & ~(DCCPF_REQUESTING | DCCPF_LISTEN) && 12462306a36Sopenharmony_ci !between48(seq, dp->dccps_awl, dp->dccps_awh)) { 12562306a36Sopenharmony_ci __NET_INC_STATS(net, LINUX_MIB_OUTOFWINDOWICMPS); 12662306a36Sopenharmony_ci goto out; 12762306a36Sopenharmony_ci } 12862306a36Sopenharmony_ci 12962306a36Sopenharmony_ci np = inet6_sk(sk); 13062306a36Sopenharmony_ci 13162306a36Sopenharmony_ci if (type == NDISC_REDIRECT) { 13262306a36Sopenharmony_ci if (!sock_owned_by_user(sk)) { 13362306a36Sopenharmony_ci struct dst_entry *dst = __sk_dst_check(sk, np->dst_cookie); 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_ci if (dst) 13662306a36Sopenharmony_ci dst->ops->redirect(dst, sk, skb); 13762306a36Sopenharmony_ci } 13862306a36Sopenharmony_ci goto out; 13962306a36Sopenharmony_ci } 14062306a36Sopenharmony_ci 14162306a36Sopenharmony_ci if (type == ICMPV6_PKT_TOOBIG) { 14262306a36Sopenharmony_ci struct dst_entry *dst = NULL; 14362306a36Sopenharmony_ci 14462306a36Sopenharmony_ci if (!ip6_sk_accept_pmtu(sk)) 14562306a36Sopenharmony_ci goto out; 14662306a36Sopenharmony_ci 14762306a36Sopenharmony_ci if (sock_owned_by_user(sk)) 14862306a36Sopenharmony_ci goto out; 14962306a36Sopenharmony_ci if ((1 << sk->sk_state) & (DCCPF_LISTEN | DCCPF_CLOSED)) 15062306a36Sopenharmony_ci goto out; 15162306a36Sopenharmony_ci 15262306a36Sopenharmony_ci dst = inet6_csk_update_pmtu(sk, ntohl(info)); 15362306a36Sopenharmony_ci if (!dst) 15462306a36Sopenharmony_ci goto out; 15562306a36Sopenharmony_ci 15662306a36Sopenharmony_ci if (inet_csk(sk)->icsk_pmtu_cookie > dst_mtu(dst)) 15762306a36Sopenharmony_ci dccp_sync_mss(sk, dst_mtu(dst)); 15862306a36Sopenharmony_ci goto out; 15962306a36Sopenharmony_ci } 16062306a36Sopenharmony_ci 16162306a36Sopenharmony_ci icmpv6_err_convert(type, code, &err); 16262306a36Sopenharmony_ci 16362306a36Sopenharmony_ci /* Might be for an request_sock */ 16462306a36Sopenharmony_ci switch (sk->sk_state) { 16562306a36Sopenharmony_ci case DCCP_REQUESTING: 16662306a36Sopenharmony_ci case DCCP_RESPOND: /* Cannot happen. 16762306a36Sopenharmony_ci It can, it SYNs are crossed. --ANK */ 16862306a36Sopenharmony_ci if (!sock_owned_by_user(sk)) { 16962306a36Sopenharmony_ci __DCCP_INC_STATS(DCCP_MIB_ATTEMPTFAILS); 17062306a36Sopenharmony_ci sk->sk_err = err; 17162306a36Sopenharmony_ci /* 17262306a36Sopenharmony_ci * Wake people up to see the error 17362306a36Sopenharmony_ci * (see connect in sock.c) 17462306a36Sopenharmony_ci */ 17562306a36Sopenharmony_ci sk_error_report(sk); 17662306a36Sopenharmony_ci dccp_done(sk); 17762306a36Sopenharmony_ci } else { 17862306a36Sopenharmony_ci WRITE_ONCE(sk->sk_err_soft, err); 17962306a36Sopenharmony_ci } 18062306a36Sopenharmony_ci goto out; 18162306a36Sopenharmony_ci } 18262306a36Sopenharmony_ci 18362306a36Sopenharmony_ci if (!sock_owned_by_user(sk) && np->recverr) { 18462306a36Sopenharmony_ci sk->sk_err = err; 18562306a36Sopenharmony_ci sk_error_report(sk); 18662306a36Sopenharmony_ci } else { 18762306a36Sopenharmony_ci WRITE_ONCE(sk->sk_err_soft, err); 18862306a36Sopenharmony_ci } 18962306a36Sopenharmony_ciout: 19062306a36Sopenharmony_ci bh_unlock_sock(sk); 19162306a36Sopenharmony_ci sock_put(sk); 19262306a36Sopenharmony_ci return 0; 19362306a36Sopenharmony_ci} 19462306a36Sopenharmony_ci 19562306a36Sopenharmony_ci 19662306a36Sopenharmony_cistatic int dccp_v6_send_response(const struct sock *sk, struct request_sock *req) 19762306a36Sopenharmony_ci{ 19862306a36Sopenharmony_ci struct inet_request_sock *ireq = inet_rsk(req); 19962306a36Sopenharmony_ci struct ipv6_pinfo *np = inet6_sk(sk); 20062306a36Sopenharmony_ci struct sk_buff *skb; 20162306a36Sopenharmony_ci struct in6_addr *final_p, final; 20262306a36Sopenharmony_ci struct flowi6 fl6; 20362306a36Sopenharmony_ci int err = -1; 20462306a36Sopenharmony_ci struct dst_entry *dst; 20562306a36Sopenharmony_ci 20662306a36Sopenharmony_ci memset(&fl6, 0, sizeof(fl6)); 20762306a36Sopenharmony_ci fl6.flowi6_proto = IPPROTO_DCCP; 20862306a36Sopenharmony_ci fl6.daddr = ireq->ir_v6_rmt_addr; 20962306a36Sopenharmony_ci fl6.saddr = ireq->ir_v6_loc_addr; 21062306a36Sopenharmony_ci fl6.flowlabel = 0; 21162306a36Sopenharmony_ci fl6.flowi6_oif = ireq->ir_iif; 21262306a36Sopenharmony_ci fl6.fl6_dport = ireq->ir_rmt_port; 21362306a36Sopenharmony_ci fl6.fl6_sport = htons(ireq->ir_num); 21462306a36Sopenharmony_ci security_req_classify_flow(req, flowi6_to_flowi_common(&fl6)); 21562306a36Sopenharmony_ci 21662306a36Sopenharmony_ci 21762306a36Sopenharmony_ci rcu_read_lock(); 21862306a36Sopenharmony_ci final_p = fl6_update_dst(&fl6, rcu_dereference(np->opt), &final); 21962306a36Sopenharmony_ci rcu_read_unlock(); 22062306a36Sopenharmony_ci 22162306a36Sopenharmony_ci dst = ip6_dst_lookup_flow(sock_net(sk), sk, &fl6, final_p); 22262306a36Sopenharmony_ci if (IS_ERR(dst)) { 22362306a36Sopenharmony_ci err = PTR_ERR(dst); 22462306a36Sopenharmony_ci dst = NULL; 22562306a36Sopenharmony_ci goto done; 22662306a36Sopenharmony_ci } 22762306a36Sopenharmony_ci 22862306a36Sopenharmony_ci skb = dccp_make_response(sk, dst, req); 22962306a36Sopenharmony_ci if (skb != NULL) { 23062306a36Sopenharmony_ci struct dccp_hdr *dh = dccp_hdr(skb); 23162306a36Sopenharmony_ci struct ipv6_txoptions *opt; 23262306a36Sopenharmony_ci 23362306a36Sopenharmony_ci dh->dccph_checksum = dccp_v6_csum_finish(skb, 23462306a36Sopenharmony_ci &ireq->ir_v6_loc_addr, 23562306a36Sopenharmony_ci &ireq->ir_v6_rmt_addr); 23662306a36Sopenharmony_ci fl6.daddr = ireq->ir_v6_rmt_addr; 23762306a36Sopenharmony_ci rcu_read_lock(); 23862306a36Sopenharmony_ci opt = ireq->ipv6_opt; 23962306a36Sopenharmony_ci if (!opt) 24062306a36Sopenharmony_ci opt = rcu_dereference(np->opt); 24162306a36Sopenharmony_ci err = ip6_xmit(sk, skb, &fl6, READ_ONCE(sk->sk_mark), opt, 24262306a36Sopenharmony_ci np->tclass, sk->sk_priority); 24362306a36Sopenharmony_ci rcu_read_unlock(); 24462306a36Sopenharmony_ci err = net_xmit_eval(err); 24562306a36Sopenharmony_ci } 24662306a36Sopenharmony_ci 24762306a36Sopenharmony_cidone: 24862306a36Sopenharmony_ci dst_release(dst); 24962306a36Sopenharmony_ci return err; 25062306a36Sopenharmony_ci} 25162306a36Sopenharmony_ci 25262306a36Sopenharmony_cistatic void dccp_v6_reqsk_destructor(struct request_sock *req) 25362306a36Sopenharmony_ci{ 25462306a36Sopenharmony_ci dccp_feat_list_purge(&dccp_rsk(req)->dreq_featneg); 25562306a36Sopenharmony_ci kfree(inet_rsk(req)->ipv6_opt); 25662306a36Sopenharmony_ci kfree_skb(inet_rsk(req)->pktopts); 25762306a36Sopenharmony_ci} 25862306a36Sopenharmony_ci 25962306a36Sopenharmony_cistatic void dccp_v6_ctl_send_reset(const struct sock *sk, struct sk_buff *rxskb) 26062306a36Sopenharmony_ci{ 26162306a36Sopenharmony_ci const struct ipv6hdr *rxip6h; 26262306a36Sopenharmony_ci struct sk_buff *skb; 26362306a36Sopenharmony_ci struct flowi6 fl6; 26462306a36Sopenharmony_ci struct net *net = dev_net(skb_dst(rxskb)->dev); 26562306a36Sopenharmony_ci struct dccp_v6_pernet *pn; 26662306a36Sopenharmony_ci struct sock *ctl_sk; 26762306a36Sopenharmony_ci struct dst_entry *dst; 26862306a36Sopenharmony_ci 26962306a36Sopenharmony_ci if (dccp_hdr(rxskb)->dccph_type == DCCP_PKT_RESET) 27062306a36Sopenharmony_ci return; 27162306a36Sopenharmony_ci 27262306a36Sopenharmony_ci if (!ipv6_unicast_destination(rxskb)) 27362306a36Sopenharmony_ci return; 27462306a36Sopenharmony_ci 27562306a36Sopenharmony_ci pn = net_generic(net, dccp_v6_pernet_id); 27662306a36Sopenharmony_ci ctl_sk = pn->v6_ctl_sk; 27762306a36Sopenharmony_ci skb = dccp_ctl_make_reset(ctl_sk, rxskb); 27862306a36Sopenharmony_ci if (skb == NULL) 27962306a36Sopenharmony_ci return; 28062306a36Sopenharmony_ci 28162306a36Sopenharmony_ci rxip6h = ipv6_hdr(rxskb); 28262306a36Sopenharmony_ci dccp_hdr(skb)->dccph_checksum = dccp_v6_csum_finish(skb, &rxip6h->saddr, 28362306a36Sopenharmony_ci &rxip6h->daddr); 28462306a36Sopenharmony_ci 28562306a36Sopenharmony_ci memset(&fl6, 0, sizeof(fl6)); 28662306a36Sopenharmony_ci fl6.daddr = rxip6h->saddr; 28762306a36Sopenharmony_ci fl6.saddr = rxip6h->daddr; 28862306a36Sopenharmony_ci 28962306a36Sopenharmony_ci fl6.flowi6_proto = IPPROTO_DCCP; 29062306a36Sopenharmony_ci fl6.flowi6_oif = inet6_iif(rxskb); 29162306a36Sopenharmony_ci fl6.fl6_dport = dccp_hdr(skb)->dccph_dport; 29262306a36Sopenharmony_ci fl6.fl6_sport = dccp_hdr(skb)->dccph_sport; 29362306a36Sopenharmony_ci security_skb_classify_flow(rxskb, flowi6_to_flowi_common(&fl6)); 29462306a36Sopenharmony_ci 29562306a36Sopenharmony_ci /* sk = NULL, but it is safe for now. RST socket required. */ 29662306a36Sopenharmony_ci dst = ip6_dst_lookup_flow(sock_net(ctl_sk), ctl_sk, &fl6, NULL); 29762306a36Sopenharmony_ci if (!IS_ERR(dst)) { 29862306a36Sopenharmony_ci skb_dst_set(skb, dst); 29962306a36Sopenharmony_ci ip6_xmit(ctl_sk, skb, &fl6, 0, NULL, 0, 0); 30062306a36Sopenharmony_ci DCCP_INC_STATS(DCCP_MIB_OUTSEGS); 30162306a36Sopenharmony_ci DCCP_INC_STATS(DCCP_MIB_OUTRSTS); 30262306a36Sopenharmony_ci return; 30362306a36Sopenharmony_ci } 30462306a36Sopenharmony_ci 30562306a36Sopenharmony_ci kfree_skb(skb); 30662306a36Sopenharmony_ci} 30762306a36Sopenharmony_ci 30862306a36Sopenharmony_cistatic struct request_sock_ops dccp6_request_sock_ops = { 30962306a36Sopenharmony_ci .family = AF_INET6, 31062306a36Sopenharmony_ci .obj_size = sizeof(struct dccp6_request_sock), 31162306a36Sopenharmony_ci .rtx_syn_ack = dccp_v6_send_response, 31262306a36Sopenharmony_ci .send_ack = dccp_reqsk_send_ack, 31362306a36Sopenharmony_ci .destructor = dccp_v6_reqsk_destructor, 31462306a36Sopenharmony_ci .send_reset = dccp_v6_ctl_send_reset, 31562306a36Sopenharmony_ci .syn_ack_timeout = dccp_syn_ack_timeout, 31662306a36Sopenharmony_ci}; 31762306a36Sopenharmony_ci 31862306a36Sopenharmony_cistatic int dccp_v6_conn_request(struct sock *sk, struct sk_buff *skb) 31962306a36Sopenharmony_ci{ 32062306a36Sopenharmony_ci struct request_sock *req; 32162306a36Sopenharmony_ci struct dccp_request_sock *dreq; 32262306a36Sopenharmony_ci struct inet_request_sock *ireq; 32362306a36Sopenharmony_ci struct ipv6_pinfo *np = inet6_sk(sk); 32462306a36Sopenharmony_ci const __be32 service = dccp_hdr_request(skb)->dccph_req_service; 32562306a36Sopenharmony_ci struct dccp_skb_cb *dcb = DCCP_SKB_CB(skb); 32662306a36Sopenharmony_ci 32762306a36Sopenharmony_ci if (skb->protocol == htons(ETH_P_IP)) 32862306a36Sopenharmony_ci return dccp_v4_conn_request(sk, skb); 32962306a36Sopenharmony_ci 33062306a36Sopenharmony_ci if (!ipv6_unicast_destination(skb)) 33162306a36Sopenharmony_ci return 0; /* discard, don't send a reset here */ 33262306a36Sopenharmony_ci 33362306a36Sopenharmony_ci if (ipv6_addr_v4mapped(&ipv6_hdr(skb)->saddr)) { 33462306a36Sopenharmony_ci __IP6_INC_STATS(sock_net(sk), NULL, IPSTATS_MIB_INHDRERRORS); 33562306a36Sopenharmony_ci return 0; 33662306a36Sopenharmony_ci } 33762306a36Sopenharmony_ci 33862306a36Sopenharmony_ci if (dccp_bad_service_code(sk, service)) { 33962306a36Sopenharmony_ci dcb->dccpd_reset_code = DCCP_RESET_CODE_BAD_SERVICE_CODE; 34062306a36Sopenharmony_ci goto drop; 34162306a36Sopenharmony_ci } 34262306a36Sopenharmony_ci /* 34362306a36Sopenharmony_ci * There are no SYN attacks on IPv6, yet... 34462306a36Sopenharmony_ci */ 34562306a36Sopenharmony_ci dcb->dccpd_reset_code = DCCP_RESET_CODE_TOO_BUSY; 34662306a36Sopenharmony_ci if (inet_csk_reqsk_queue_is_full(sk)) 34762306a36Sopenharmony_ci goto drop; 34862306a36Sopenharmony_ci 34962306a36Sopenharmony_ci if (sk_acceptq_is_full(sk)) 35062306a36Sopenharmony_ci goto drop; 35162306a36Sopenharmony_ci 35262306a36Sopenharmony_ci req = inet_reqsk_alloc(&dccp6_request_sock_ops, sk, true); 35362306a36Sopenharmony_ci if (req == NULL) 35462306a36Sopenharmony_ci goto drop; 35562306a36Sopenharmony_ci 35662306a36Sopenharmony_ci if (dccp_reqsk_init(req, dccp_sk(sk), skb)) 35762306a36Sopenharmony_ci goto drop_and_free; 35862306a36Sopenharmony_ci 35962306a36Sopenharmony_ci dreq = dccp_rsk(req); 36062306a36Sopenharmony_ci if (dccp_parse_options(sk, dreq, skb)) 36162306a36Sopenharmony_ci goto drop_and_free; 36262306a36Sopenharmony_ci 36362306a36Sopenharmony_ci ireq = inet_rsk(req); 36462306a36Sopenharmony_ci ireq->ir_v6_rmt_addr = ipv6_hdr(skb)->saddr; 36562306a36Sopenharmony_ci ireq->ir_v6_loc_addr = ipv6_hdr(skb)->daddr; 36662306a36Sopenharmony_ci ireq->ireq_family = AF_INET6; 36762306a36Sopenharmony_ci ireq->ir_mark = inet_request_mark(sk, skb); 36862306a36Sopenharmony_ci 36962306a36Sopenharmony_ci if (security_inet_conn_request(sk, skb, req)) 37062306a36Sopenharmony_ci goto drop_and_free; 37162306a36Sopenharmony_ci 37262306a36Sopenharmony_ci if (ipv6_opt_accepted(sk, skb, IP6CB(skb)) || 37362306a36Sopenharmony_ci np->rxopt.bits.rxinfo || np->rxopt.bits.rxoinfo || 37462306a36Sopenharmony_ci np->rxopt.bits.rxhlim || np->rxopt.bits.rxohlim) { 37562306a36Sopenharmony_ci refcount_inc(&skb->users); 37662306a36Sopenharmony_ci ireq->pktopts = skb; 37762306a36Sopenharmony_ci } 37862306a36Sopenharmony_ci ireq->ir_iif = READ_ONCE(sk->sk_bound_dev_if); 37962306a36Sopenharmony_ci 38062306a36Sopenharmony_ci /* So that link locals have meaning */ 38162306a36Sopenharmony_ci if (!ireq->ir_iif && 38262306a36Sopenharmony_ci ipv6_addr_type(&ireq->ir_v6_rmt_addr) & IPV6_ADDR_LINKLOCAL) 38362306a36Sopenharmony_ci ireq->ir_iif = inet6_iif(skb); 38462306a36Sopenharmony_ci 38562306a36Sopenharmony_ci /* 38662306a36Sopenharmony_ci * Step 3: Process LISTEN state 38762306a36Sopenharmony_ci * 38862306a36Sopenharmony_ci * Set S.ISR, S.GSR, S.SWL, S.SWH from packet or Init Cookie 38962306a36Sopenharmony_ci * 39062306a36Sopenharmony_ci * Setting S.SWL/S.SWH to is deferred to dccp_create_openreq_child(). 39162306a36Sopenharmony_ci */ 39262306a36Sopenharmony_ci dreq->dreq_isr = dcb->dccpd_seq; 39362306a36Sopenharmony_ci dreq->dreq_gsr = dreq->dreq_isr; 39462306a36Sopenharmony_ci dreq->dreq_iss = dccp_v6_init_sequence(skb); 39562306a36Sopenharmony_ci dreq->dreq_gss = dreq->dreq_iss; 39662306a36Sopenharmony_ci dreq->dreq_service = service; 39762306a36Sopenharmony_ci 39862306a36Sopenharmony_ci if (dccp_v6_send_response(sk, req)) 39962306a36Sopenharmony_ci goto drop_and_free; 40062306a36Sopenharmony_ci 40162306a36Sopenharmony_ci inet_csk_reqsk_queue_hash_add(sk, req, DCCP_TIMEOUT_INIT); 40262306a36Sopenharmony_ci reqsk_put(req); 40362306a36Sopenharmony_ci return 0; 40462306a36Sopenharmony_ci 40562306a36Sopenharmony_cidrop_and_free: 40662306a36Sopenharmony_ci reqsk_free(req); 40762306a36Sopenharmony_cidrop: 40862306a36Sopenharmony_ci __DCCP_INC_STATS(DCCP_MIB_ATTEMPTFAILS); 40962306a36Sopenharmony_ci return -1; 41062306a36Sopenharmony_ci} 41162306a36Sopenharmony_ci 41262306a36Sopenharmony_cistatic struct sock *dccp_v6_request_recv_sock(const struct sock *sk, 41362306a36Sopenharmony_ci struct sk_buff *skb, 41462306a36Sopenharmony_ci struct request_sock *req, 41562306a36Sopenharmony_ci struct dst_entry *dst, 41662306a36Sopenharmony_ci struct request_sock *req_unhash, 41762306a36Sopenharmony_ci bool *own_req) 41862306a36Sopenharmony_ci{ 41962306a36Sopenharmony_ci struct inet_request_sock *ireq = inet_rsk(req); 42062306a36Sopenharmony_ci struct ipv6_pinfo *newnp; 42162306a36Sopenharmony_ci const struct ipv6_pinfo *np = inet6_sk(sk); 42262306a36Sopenharmony_ci struct ipv6_txoptions *opt; 42362306a36Sopenharmony_ci struct inet_sock *newinet; 42462306a36Sopenharmony_ci struct dccp6_sock *newdp6; 42562306a36Sopenharmony_ci struct sock *newsk; 42662306a36Sopenharmony_ci 42762306a36Sopenharmony_ci if (skb->protocol == htons(ETH_P_IP)) { 42862306a36Sopenharmony_ci /* 42962306a36Sopenharmony_ci * v6 mapped 43062306a36Sopenharmony_ci */ 43162306a36Sopenharmony_ci newsk = dccp_v4_request_recv_sock(sk, skb, req, dst, 43262306a36Sopenharmony_ci req_unhash, own_req); 43362306a36Sopenharmony_ci if (newsk == NULL) 43462306a36Sopenharmony_ci return NULL; 43562306a36Sopenharmony_ci 43662306a36Sopenharmony_ci newdp6 = (struct dccp6_sock *)newsk; 43762306a36Sopenharmony_ci newinet = inet_sk(newsk); 43862306a36Sopenharmony_ci newinet->pinet6 = &newdp6->inet6; 43962306a36Sopenharmony_ci newnp = inet6_sk(newsk); 44062306a36Sopenharmony_ci 44162306a36Sopenharmony_ci memcpy(newnp, np, sizeof(struct ipv6_pinfo)); 44262306a36Sopenharmony_ci 44362306a36Sopenharmony_ci newnp->saddr = newsk->sk_v6_rcv_saddr; 44462306a36Sopenharmony_ci 44562306a36Sopenharmony_ci inet_csk(newsk)->icsk_af_ops = &dccp_ipv6_mapped; 44662306a36Sopenharmony_ci newsk->sk_backlog_rcv = dccp_v4_do_rcv; 44762306a36Sopenharmony_ci newnp->pktoptions = NULL; 44862306a36Sopenharmony_ci newnp->opt = NULL; 44962306a36Sopenharmony_ci newnp->ipv6_mc_list = NULL; 45062306a36Sopenharmony_ci newnp->ipv6_ac_list = NULL; 45162306a36Sopenharmony_ci newnp->ipv6_fl_list = NULL; 45262306a36Sopenharmony_ci newnp->mcast_oif = inet_iif(skb); 45362306a36Sopenharmony_ci newnp->mcast_hops = ip_hdr(skb)->ttl; 45462306a36Sopenharmony_ci 45562306a36Sopenharmony_ci /* 45662306a36Sopenharmony_ci * No need to charge this sock to the relevant IPv6 refcnt debug socks count 45762306a36Sopenharmony_ci * here, dccp_create_openreq_child now does this for us, see the comment in 45862306a36Sopenharmony_ci * that function for the gory details. -acme 45962306a36Sopenharmony_ci */ 46062306a36Sopenharmony_ci 46162306a36Sopenharmony_ci /* It is tricky place. Until this moment IPv4 tcp 46262306a36Sopenharmony_ci worked with IPv6 icsk.icsk_af_ops. 46362306a36Sopenharmony_ci Sync it now. 46462306a36Sopenharmony_ci */ 46562306a36Sopenharmony_ci dccp_sync_mss(newsk, inet_csk(newsk)->icsk_pmtu_cookie); 46662306a36Sopenharmony_ci 46762306a36Sopenharmony_ci return newsk; 46862306a36Sopenharmony_ci } 46962306a36Sopenharmony_ci 47062306a36Sopenharmony_ci 47162306a36Sopenharmony_ci if (sk_acceptq_is_full(sk)) 47262306a36Sopenharmony_ci goto out_overflow; 47362306a36Sopenharmony_ci 47462306a36Sopenharmony_ci if (!dst) { 47562306a36Sopenharmony_ci struct flowi6 fl6; 47662306a36Sopenharmony_ci 47762306a36Sopenharmony_ci dst = inet6_csk_route_req(sk, &fl6, req, IPPROTO_DCCP); 47862306a36Sopenharmony_ci if (!dst) 47962306a36Sopenharmony_ci goto out; 48062306a36Sopenharmony_ci } 48162306a36Sopenharmony_ci 48262306a36Sopenharmony_ci newsk = dccp_create_openreq_child(sk, req, skb); 48362306a36Sopenharmony_ci if (newsk == NULL) 48462306a36Sopenharmony_ci goto out_nonewsk; 48562306a36Sopenharmony_ci 48662306a36Sopenharmony_ci /* 48762306a36Sopenharmony_ci * No need to charge this sock to the relevant IPv6 refcnt debug socks 48862306a36Sopenharmony_ci * count here, dccp_create_openreq_child now does this for us, see the 48962306a36Sopenharmony_ci * comment in that function for the gory details. -acme 49062306a36Sopenharmony_ci */ 49162306a36Sopenharmony_ci 49262306a36Sopenharmony_ci ip6_dst_store(newsk, dst, NULL, NULL); 49362306a36Sopenharmony_ci newsk->sk_route_caps = dst->dev->features & ~(NETIF_F_IP_CSUM | 49462306a36Sopenharmony_ci NETIF_F_TSO); 49562306a36Sopenharmony_ci newdp6 = (struct dccp6_sock *)newsk; 49662306a36Sopenharmony_ci newinet = inet_sk(newsk); 49762306a36Sopenharmony_ci newinet->pinet6 = &newdp6->inet6; 49862306a36Sopenharmony_ci newnp = inet6_sk(newsk); 49962306a36Sopenharmony_ci 50062306a36Sopenharmony_ci memcpy(newnp, np, sizeof(struct ipv6_pinfo)); 50162306a36Sopenharmony_ci 50262306a36Sopenharmony_ci newsk->sk_v6_daddr = ireq->ir_v6_rmt_addr; 50362306a36Sopenharmony_ci newnp->saddr = ireq->ir_v6_loc_addr; 50462306a36Sopenharmony_ci newsk->sk_v6_rcv_saddr = ireq->ir_v6_loc_addr; 50562306a36Sopenharmony_ci newsk->sk_bound_dev_if = ireq->ir_iif; 50662306a36Sopenharmony_ci 50762306a36Sopenharmony_ci /* Now IPv6 options... 50862306a36Sopenharmony_ci 50962306a36Sopenharmony_ci First: no IPv4 options. 51062306a36Sopenharmony_ci */ 51162306a36Sopenharmony_ci newinet->inet_opt = NULL; 51262306a36Sopenharmony_ci 51362306a36Sopenharmony_ci /* Clone RX bits */ 51462306a36Sopenharmony_ci newnp->rxopt.all = np->rxopt.all; 51562306a36Sopenharmony_ci 51662306a36Sopenharmony_ci newnp->ipv6_mc_list = NULL; 51762306a36Sopenharmony_ci newnp->ipv6_ac_list = NULL; 51862306a36Sopenharmony_ci newnp->ipv6_fl_list = NULL; 51962306a36Sopenharmony_ci newnp->pktoptions = NULL; 52062306a36Sopenharmony_ci newnp->opt = NULL; 52162306a36Sopenharmony_ci newnp->mcast_oif = inet6_iif(skb); 52262306a36Sopenharmony_ci newnp->mcast_hops = ipv6_hdr(skb)->hop_limit; 52362306a36Sopenharmony_ci 52462306a36Sopenharmony_ci /* 52562306a36Sopenharmony_ci * Clone native IPv6 options from listening socket (if any) 52662306a36Sopenharmony_ci * 52762306a36Sopenharmony_ci * Yes, keeping reference count would be much more clever, but we make 52862306a36Sopenharmony_ci * one more one thing there: reattach optmem to newsk. 52962306a36Sopenharmony_ci */ 53062306a36Sopenharmony_ci opt = ireq->ipv6_opt; 53162306a36Sopenharmony_ci if (!opt) 53262306a36Sopenharmony_ci opt = rcu_dereference(np->opt); 53362306a36Sopenharmony_ci if (opt) { 53462306a36Sopenharmony_ci opt = ipv6_dup_options(newsk, opt); 53562306a36Sopenharmony_ci RCU_INIT_POINTER(newnp->opt, opt); 53662306a36Sopenharmony_ci } 53762306a36Sopenharmony_ci inet_csk(newsk)->icsk_ext_hdr_len = 0; 53862306a36Sopenharmony_ci if (opt) 53962306a36Sopenharmony_ci inet_csk(newsk)->icsk_ext_hdr_len = opt->opt_nflen + 54062306a36Sopenharmony_ci opt->opt_flen; 54162306a36Sopenharmony_ci 54262306a36Sopenharmony_ci dccp_sync_mss(newsk, dst_mtu(dst)); 54362306a36Sopenharmony_ci 54462306a36Sopenharmony_ci newinet->inet_daddr = newinet->inet_saddr = LOOPBACK4_IPV6; 54562306a36Sopenharmony_ci newinet->inet_rcv_saddr = LOOPBACK4_IPV6; 54662306a36Sopenharmony_ci 54762306a36Sopenharmony_ci if (__inet_inherit_port(sk, newsk) < 0) { 54862306a36Sopenharmony_ci inet_csk_prepare_forced_close(newsk); 54962306a36Sopenharmony_ci dccp_done(newsk); 55062306a36Sopenharmony_ci goto out; 55162306a36Sopenharmony_ci } 55262306a36Sopenharmony_ci *own_req = inet_ehash_nolisten(newsk, req_to_sk(req_unhash), NULL); 55362306a36Sopenharmony_ci /* Clone pktoptions received with SYN, if we own the req */ 55462306a36Sopenharmony_ci if (*own_req && ireq->pktopts) { 55562306a36Sopenharmony_ci newnp->pktoptions = skb_clone_and_charge_r(ireq->pktopts, newsk); 55662306a36Sopenharmony_ci consume_skb(ireq->pktopts); 55762306a36Sopenharmony_ci ireq->pktopts = NULL; 55862306a36Sopenharmony_ci } 55962306a36Sopenharmony_ci 56062306a36Sopenharmony_ci return newsk; 56162306a36Sopenharmony_ci 56262306a36Sopenharmony_ciout_overflow: 56362306a36Sopenharmony_ci __NET_INC_STATS(sock_net(sk), LINUX_MIB_LISTENOVERFLOWS); 56462306a36Sopenharmony_ciout_nonewsk: 56562306a36Sopenharmony_ci dst_release(dst); 56662306a36Sopenharmony_ciout: 56762306a36Sopenharmony_ci __NET_INC_STATS(sock_net(sk), LINUX_MIB_LISTENDROPS); 56862306a36Sopenharmony_ci return NULL; 56962306a36Sopenharmony_ci} 57062306a36Sopenharmony_ci 57162306a36Sopenharmony_ci/* The socket must have it's spinlock held when we get 57262306a36Sopenharmony_ci * here. 57362306a36Sopenharmony_ci * 57462306a36Sopenharmony_ci * We have a potential double-lock case here, so even when 57562306a36Sopenharmony_ci * doing backlog processing we use the BH locking scheme. 57662306a36Sopenharmony_ci * This is because we cannot sleep with the original spinlock 57762306a36Sopenharmony_ci * held. 57862306a36Sopenharmony_ci */ 57962306a36Sopenharmony_cistatic int dccp_v6_do_rcv(struct sock *sk, struct sk_buff *skb) 58062306a36Sopenharmony_ci{ 58162306a36Sopenharmony_ci struct ipv6_pinfo *np = inet6_sk(sk); 58262306a36Sopenharmony_ci struct sk_buff *opt_skb = NULL; 58362306a36Sopenharmony_ci 58462306a36Sopenharmony_ci /* Imagine: socket is IPv6. IPv4 packet arrives, 58562306a36Sopenharmony_ci goes to IPv4 receive handler and backlogged. 58662306a36Sopenharmony_ci From backlog it always goes here. Kerboom... 58762306a36Sopenharmony_ci Fortunately, dccp_rcv_established and rcv_established 58862306a36Sopenharmony_ci handle them correctly, but it is not case with 58962306a36Sopenharmony_ci dccp_v6_hnd_req and dccp_v6_ctl_send_reset(). --ANK 59062306a36Sopenharmony_ci */ 59162306a36Sopenharmony_ci 59262306a36Sopenharmony_ci if (skb->protocol == htons(ETH_P_IP)) 59362306a36Sopenharmony_ci return dccp_v4_do_rcv(sk, skb); 59462306a36Sopenharmony_ci 59562306a36Sopenharmony_ci if (sk_filter(sk, skb)) 59662306a36Sopenharmony_ci goto discard; 59762306a36Sopenharmony_ci 59862306a36Sopenharmony_ci /* 59962306a36Sopenharmony_ci * socket locking is here for SMP purposes as backlog rcv is currently 60062306a36Sopenharmony_ci * called with bh processing disabled. 60162306a36Sopenharmony_ci */ 60262306a36Sopenharmony_ci 60362306a36Sopenharmony_ci /* Do Stevens' IPV6_PKTOPTIONS. 60462306a36Sopenharmony_ci 60562306a36Sopenharmony_ci Yes, guys, it is the only place in our code, where we 60662306a36Sopenharmony_ci may make it not affecting IPv4. 60762306a36Sopenharmony_ci The rest of code is protocol independent, 60862306a36Sopenharmony_ci and I do not like idea to uglify IPv4. 60962306a36Sopenharmony_ci 61062306a36Sopenharmony_ci Actually, all the idea behind IPV6_PKTOPTIONS 61162306a36Sopenharmony_ci looks not very well thought. For now we latch 61262306a36Sopenharmony_ci options, received in the last packet, enqueued 61362306a36Sopenharmony_ci by tcp. Feel free to propose better solution. 61462306a36Sopenharmony_ci --ANK (980728) 61562306a36Sopenharmony_ci */ 61662306a36Sopenharmony_ci if (np->rxopt.all) 61762306a36Sopenharmony_ci opt_skb = skb_clone_and_charge_r(skb, sk); 61862306a36Sopenharmony_ci 61962306a36Sopenharmony_ci if (sk->sk_state == DCCP_OPEN) { /* Fast path */ 62062306a36Sopenharmony_ci if (dccp_rcv_established(sk, skb, dccp_hdr(skb), skb->len)) 62162306a36Sopenharmony_ci goto reset; 62262306a36Sopenharmony_ci if (opt_skb) 62362306a36Sopenharmony_ci goto ipv6_pktoptions; 62462306a36Sopenharmony_ci return 0; 62562306a36Sopenharmony_ci } 62662306a36Sopenharmony_ci 62762306a36Sopenharmony_ci /* 62862306a36Sopenharmony_ci * Step 3: Process LISTEN state 62962306a36Sopenharmony_ci * If S.state == LISTEN, 63062306a36Sopenharmony_ci * If P.type == Request or P contains a valid Init Cookie option, 63162306a36Sopenharmony_ci * (* Must scan the packet's options to check for Init 63262306a36Sopenharmony_ci * Cookies. Only Init Cookies are processed here, 63362306a36Sopenharmony_ci * however; other options are processed in Step 8. This 63462306a36Sopenharmony_ci * scan need only be performed if the endpoint uses Init 63562306a36Sopenharmony_ci * Cookies *) 63662306a36Sopenharmony_ci * (* Generate a new socket and switch to that socket *) 63762306a36Sopenharmony_ci * Set S := new socket for this port pair 63862306a36Sopenharmony_ci * S.state = RESPOND 63962306a36Sopenharmony_ci * Choose S.ISS (initial seqno) or set from Init Cookies 64062306a36Sopenharmony_ci * Initialize S.GAR := S.ISS 64162306a36Sopenharmony_ci * Set S.ISR, S.GSR, S.SWL, S.SWH from packet or Init Cookies 64262306a36Sopenharmony_ci * Continue with S.state == RESPOND 64362306a36Sopenharmony_ci * (* A Response packet will be generated in Step 11 *) 64462306a36Sopenharmony_ci * Otherwise, 64562306a36Sopenharmony_ci * Generate Reset(No Connection) unless P.type == Reset 64662306a36Sopenharmony_ci * Drop packet and return 64762306a36Sopenharmony_ci * 64862306a36Sopenharmony_ci * NOTE: the check for the packet types is done in 64962306a36Sopenharmony_ci * dccp_rcv_state_process 65062306a36Sopenharmony_ci */ 65162306a36Sopenharmony_ci 65262306a36Sopenharmony_ci if (dccp_rcv_state_process(sk, skb, dccp_hdr(skb), skb->len)) 65362306a36Sopenharmony_ci goto reset; 65462306a36Sopenharmony_ci if (opt_skb) 65562306a36Sopenharmony_ci goto ipv6_pktoptions; 65662306a36Sopenharmony_ci return 0; 65762306a36Sopenharmony_ci 65862306a36Sopenharmony_cireset: 65962306a36Sopenharmony_ci dccp_v6_ctl_send_reset(sk, skb); 66062306a36Sopenharmony_cidiscard: 66162306a36Sopenharmony_ci if (opt_skb != NULL) 66262306a36Sopenharmony_ci __kfree_skb(opt_skb); 66362306a36Sopenharmony_ci kfree_skb(skb); 66462306a36Sopenharmony_ci return 0; 66562306a36Sopenharmony_ci 66662306a36Sopenharmony_ci/* Handling IPV6_PKTOPTIONS skb the similar 66762306a36Sopenharmony_ci * way it's done for net/ipv6/tcp_ipv6.c 66862306a36Sopenharmony_ci */ 66962306a36Sopenharmony_ciipv6_pktoptions: 67062306a36Sopenharmony_ci if (!((1 << sk->sk_state) & (DCCPF_CLOSED | DCCPF_LISTEN))) { 67162306a36Sopenharmony_ci if (np->rxopt.bits.rxinfo || np->rxopt.bits.rxoinfo) 67262306a36Sopenharmony_ci np->mcast_oif = inet6_iif(opt_skb); 67362306a36Sopenharmony_ci if (np->rxopt.bits.rxhlim || np->rxopt.bits.rxohlim) 67462306a36Sopenharmony_ci np->mcast_hops = ipv6_hdr(opt_skb)->hop_limit; 67562306a36Sopenharmony_ci if (np->rxopt.bits.rxflow || np->rxopt.bits.rxtclass) 67662306a36Sopenharmony_ci np->rcv_flowinfo = ip6_flowinfo(ipv6_hdr(opt_skb)); 67762306a36Sopenharmony_ci if (np->repflow) 67862306a36Sopenharmony_ci np->flow_label = ip6_flowlabel(ipv6_hdr(opt_skb)); 67962306a36Sopenharmony_ci if (ipv6_opt_accepted(sk, opt_skb, 68062306a36Sopenharmony_ci &DCCP_SKB_CB(opt_skb)->header.h6)) { 68162306a36Sopenharmony_ci memmove(IP6CB(opt_skb), 68262306a36Sopenharmony_ci &DCCP_SKB_CB(opt_skb)->header.h6, 68362306a36Sopenharmony_ci sizeof(struct inet6_skb_parm)); 68462306a36Sopenharmony_ci opt_skb = xchg(&np->pktoptions, opt_skb); 68562306a36Sopenharmony_ci } else { 68662306a36Sopenharmony_ci __kfree_skb(opt_skb); 68762306a36Sopenharmony_ci opt_skb = xchg(&np->pktoptions, NULL); 68862306a36Sopenharmony_ci } 68962306a36Sopenharmony_ci } 69062306a36Sopenharmony_ci 69162306a36Sopenharmony_ci kfree_skb(opt_skb); 69262306a36Sopenharmony_ci return 0; 69362306a36Sopenharmony_ci} 69462306a36Sopenharmony_ci 69562306a36Sopenharmony_cistatic int dccp_v6_rcv(struct sk_buff *skb) 69662306a36Sopenharmony_ci{ 69762306a36Sopenharmony_ci const struct dccp_hdr *dh; 69862306a36Sopenharmony_ci bool refcounted; 69962306a36Sopenharmony_ci struct sock *sk; 70062306a36Sopenharmony_ci int min_cov; 70162306a36Sopenharmony_ci 70262306a36Sopenharmony_ci /* Step 1: Check header basics */ 70362306a36Sopenharmony_ci 70462306a36Sopenharmony_ci if (dccp_invalid_packet(skb)) 70562306a36Sopenharmony_ci goto discard_it; 70662306a36Sopenharmony_ci 70762306a36Sopenharmony_ci /* Step 1: If header checksum is incorrect, drop packet and return. */ 70862306a36Sopenharmony_ci if (dccp_v6_csum_finish(skb, &ipv6_hdr(skb)->saddr, 70962306a36Sopenharmony_ci &ipv6_hdr(skb)->daddr)) { 71062306a36Sopenharmony_ci DCCP_WARN("dropped packet with invalid checksum\n"); 71162306a36Sopenharmony_ci goto discard_it; 71262306a36Sopenharmony_ci } 71362306a36Sopenharmony_ci 71462306a36Sopenharmony_ci dh = dccp_hdr(skb); 71562306a36Sopenharmony_ci 71662306a36Sopenharmony_ci DCCP_SKB_CB(skb)->dccpd_seq = dccp_hdr_seq(dh); 71762306a36Sopenharmony_ci DCCP_SKB_CB(skb)->dccpd_type = dh->dccph_type; 71862306a36Sopenharmony_ci 71962306a36Sopenharmony_ci if (dccp_packet_without_ack(skb)) 72062306a36Sopenharmony_ci DCCP_SKB_CB(skb)->dccpd_ack_seq = DCCP_PKT_WITHOUT_ACK_SEQ; 72162306a36Sopenharmony_ci else 72262306a36Sopenharmony_ci DCCP_SKB_CB(skb)->dccpd_ack_seq = dccp_hdr_ack_seq(skb); 72362306a36Sopenharmony_ci 72462306a36Sopenharmony_cilookup: 72562306a36Sopenharmony_ci sk = __inet6_lookup_skb(&dccp_hashinfo, skb, __dccp_hdr_len(dh), 72662306a36Sopenharmony_ci dh->dccph_sport, dh->dccph_dport, 72762306a36Sopenharmony_ci inet6_iif(skb), 0, &refcounted); 72862306a36Sopenharmony_ci if (!sk) { 72962306a36Sopenharmony_ci dccp_pr_debug("failed to look up flow ID in table and " 73062306a36Sopenharmony_ci "get corresponding socket\n"); 73162306a36Sopenharmony_ci goto no_dccp_socket; 73262306a36Sopenharmony_ci } 73362306a36Sopenharmony_ci 73462306a36Sopenharmony_ci /* 73562306a36Sopenharmony_ci * Step 2: 73662306a36Sopenharmony_ci * ... or S.state == TIMEWAIT, 73762306a36Sopenharmony_ci * Generate Reset(No Connection) unless P.type == Reset 73862306a36Sopenharmony_ci * Drop packet and return 73962306a36Sopenharmony_ci */ 74062306a36Sopenharmony_ci if (sk->sk_state == DCCP_TIME_WAIT) { 74162306a36Sopenharmony_ci dccp_pr_debug("sk->sk_state == DCCP_TIME_WAIT: do_time_wait\n"); 74262306a36Sopenharmony_ci inet_twsk_put(inet_twsk(sk)); 74362306a36Sopenharmony_ci goto no_dccp_socket; 74462306a36Sopenharmony_ci } 74562306a36Sopenharmony_ci 74662306a36Sopenharmony_ci if (sk->sk_state == DCCP_NEW_SYN_RECV) { 74762306a36Sopenharmony_ci struct request_sock *req = inet_reqsk(sk); 74862306a36Sopenharmony_ci struct sock *nsk; 74962306a36Sopenharmony_ci 75062306a36Sopenharmony_ci sk = req->rsk_listener; 75162306a36Sopenharmony_ci if (unlikely(sk->sk_state != DCCP_LISTEN)) { 75262306a36Sopenharmony_ci inet_csk_reqsk_queue_drop_and_put(sk, req); 75362306a36Sopenharmony_ci goto lookup; 75462306a36Sopenharmony_ci } 75562306a36Sopenharmony_ci sock_hold(sk); 75662306a36Sopenharmony_ci refcounted = true; 75762306a36Sopenharmony_ci nsk = dccp_check_req(sk, skb, req); 75862306a36Sopenharmony_ci if (!nsk) { 75962306a36Sopenharmony_ci reqsk_put(req); 76062306a36Sopenharmony_ci goto discard_and_relse; 76162306a36Sopenharmony_ci } 76262306a36Sopenharmony_ci if (nsk == sk) { 76362306a36Sopenharmony_ci reqsk_put(req); 76462306a36Sopenharmony_ci } else if (dccp_child_process(sk, nsk, skb)) { 76562306a36Sopenharmony_ci dccp_v6_ctl_send_reset(sk, skb); 76662306a36Sopenharmony_ci goto discard_and_relse; 76762306a36Sopenharmony_ci } else { 76862306a36Sopenharmony_ci sock_put(sk); 76962306a36Sopenharmony_ci return 0; 77062306a36Sopenharmony_ci } 77162306a36Sopenharmony_ci } 77262306a36Sopenharmony_ci /* 77362306a36Sopenharmony_ci * RFC 4340, sec. 9.2.1: Minimum Checksum Coverage 77462306a36Sopenharmony_ci * o if MinCsCov = 0, only packets with CsCov = 0 are accepted 77562306a36Sopenharmony_ci * o if MinCsCov > 0, also accept packets with CsCov >= MinCsCov 77662306a36Sopenharmony_ci */ 77762306a36Sopenharmony_ci min_cov = dccp_sk(sk)->dccps_pcrlen; 77862306a36Sopenharmony_ci if (dh->dccph_cscov && (min_cov == 0 || dh->dccph_cscov < min_cov)) { 77962306a36Sopenharmony_ci dccp_pr_debug("Packet CsCov %d does not satisfy MinCsCov %d\n", 78062306a36Sopenharmony_ci dh->dccph_cscov, min_cov); 78162306a36Sopenharmony_ci /* FIXME: send Data Dropped option (see also dccp_v4_rcv) */ 78262306a36Sopenharmony_ci goto discard_and_relse; 78362306a36Sopenharmony_ci } 78462306a36Sopenharmony_ci 78562306a36Sopenharmony_ci if (!xfrm6_policy_check(sk, XFRM_POLICY_IN, skb)) 78662306a36Sopenharmony_ci goto discard_and_relse; 78762306a36Sopenharmony_ci nf_reset_ct(skb); 78862306a36Sopenharmony_ci 78962306a36Sopenharmony_ci return __sk_receive_skb(sk, skb, 1, dh->dccph_doff * 4, 79062306a36Sopenharmony_ci refcounted) ? -1 : 0; 79162306a36Sopenharmony_ci 79262306a36Sopenharmony_cino_dccp_socket: 79362306a36Sopenharmony_ci if (!xfrm6_policy_check(NULL, XFRM_POLICY_IN, skb)) 79462306a36Sopenharmony_ci goto discard_it; 79562306a36Sopenharmony_ci /* 79662306a36Sopenharmony_ci * Step 2: 79762306a36Sopenharmony_ci * If no socket ... 79862306a36Sopenharmony_ci * Generate Reset(No Connection) unless P.type == Reset 79962306a36Sopenharmony_ci * Drop packet and return 80062306a36Sopenharmony_ci */ 80162306a36Sopenharmony_ci if (dh->dccph_type != DCCP_PKT_RESET) { 80262306a36Sopenharmony_ci DCCP_SKB_CB(skb)->dccpd_reset_code = 80362306a36Sopenharmony_ci DCCP_RESET_CODE_NO_CONNECTION; 80462306a36Sopenharmony_ci dccp_v6_ctl_send_reset(sk, skb); 80562306a36Sopenharmony_ci } 80662306a36Sopenharmony_ci 80762306a36Sopenharmony_cidiscard_it: 80862306a36Sopenharmony_ci kfree_skb(skb); 80962306a36Sopenharmony_ci return 0; 81062306a36Sopenharmony_ci 81162306a36Sopenharmony_cidiscard_and_relse: 81262306a36Sopenharmony_ci if (refcounted) 81362306a36Sopenharmony_ci sock_put(sk); 81462306a36Sopenharmony_ci goto discard_it; 81562306a36Sopenharmony_ci} 81662306a36Sopenharmony_ci 81762306a36Sopenharmony_cistatic int dccp_v6_connect(struct sock *sk, struct sockaddr *uaddr, 81862306a36Sopenharmony_ci int addr_len) 81962306a36Sopenharmony_ci{ 82062306a36Sopenharmony_ci struct sockaddr_in6 *usin = (struct sockaddr_in6 *)uaddr; 82162306a36Sopenharmony_ci struct inet_connection_sock *icsk = inet_csk(sk); 82262306a36Sopenharmony_ci struct inet_sock *inet = inet_sk(sk); 82362306a36Sopenharmony_ci struct ipv6_pinfo *np = inet6_sk(sk); 82462306a36Sopenharmony_ci struct dccp_sock *dp = dccp_sk(sk); 82562306a36Sopenharmony_ci struct in6_addr *saddr = NULL, *final_p, final; 82662306a36Sopenharmony_ci struct ipv6_txoptions *opt; 82762306a36Sopenharmony_ci struct flowi6 fl6; 82862306a36Sopenharmony_ci struct dst_entry *dst; 82962306a36Sopenharmony_ci int addr_type; 83062306a36Sopenharmony_ci int err; 83162306a36Sopenharmony_ci 83262306a36Sopenharmony_ci dp->dccps_role = DCCP_ROLE_CLIENT; 83362306a36Sopenharmony_ci 83462306a36Sopenharmony_ci if (addr_len < SIN6_LEN_RFC2133) 83562306a36Sopenharmony_ci return -EINVAL; 83662306a36Sopenharmony_ci 83762306a36Sopenharmony_ci if (usin->sin6_family != AF_INET6) 83862306a36Sopenharmony_ci return -EAFNOSUPPORT; 83962306a36Sopenharmony_ci 84062306a36Sopenharmony_ci memset(&fl6, 0, sizeof(fl6)); 84162306a36Sopenharmony_ci 84262306a36Sopenharmony_ci if (np->sndflow) { 84362306a36Sopenharmony_ci fl6.flowlabel = usin->sin6_flowinfo & IPV6_FLOWINFO_MASK; 84462306a36Sopenharmony_ci IP6_ECN_flow_init(fl6.flowlabel); 84562306a36Sopenharmony_ci if (fl6.flowlabel & IPV6_FLOWLABEL_MASK) { 84662306a36Sopenharmony_ci struct ip6_flowlabel *flowlabel; 84762306a36Sopenharmony_ci flowlabel = fl6_sock_lookup(sk, fl6.flowlabel); 84862306a36Sopenharmony_ci if (IS_ERR(flowlabel)) 84962306a36Sopenharmony_ci return -EINVAL; 85062306a36Sopenharmony_ci fl6_sock_release(flowlabel); 85162306a36Sopenharmony_ci } 85262306a36Sopenharmony_ci } 85362306a36Sopenharmony_ci /* 85462306a36Sopenharmony_ci * connect() to INADDR_ANY means loopback (BSD'ism). 85562306a36Sopenharmony_ci */ 85662306a36Sopenharmony_ci if (ipv6_addr_any(&usin->sin6_addr)) 85762306a36Sopenharmony_ci usin->sin6_addr.s6_addr[15] = 1; 85862306a36Sopenharmony_ci 85962306a36Sopenharmony_ci addr_type = ipv6_addr_type(&usin->sin6_addr); 86062306a36Sopenharmony_ci 86162306a36Sopenharmony_ci if (addr_type & IPV6_ADDR_MULTICAST) 86262306a36Sopenharmony_ci return -ENETUNREACH; 86362306a36Sopenharmony_ci 86462306a36Sopenharmony_ci if (addr_type & IPV6_ADDR_LINKLOCAL) { 86562306a36Sopenharmony_ci if (addr_len >= sizeof(struct sockaddr_in6) && 86662306a36Sopenharmony_ci usin->sin6_scope_id) { 86762306a36Sopenharmony_ci /* If interface is set while binding, indices 86862306a36Sopenharmony_ci * must coincide. 86962306a36Sopenharmony_ci */ 87062306a36Sopenharmony_ci if (sk->sk_bound_dev_if && 87162306a36Sopenharmony_ci sk->sk_bound_dev_if != usin->sin6_scope_id) 87262306a36Sopenharmony_ci return -EINVAL; 87362306a36Sopenharmony_ci 87462306a36Sopenharmony_ci sk->sk_bound_dev_if = usin->sin6_scope_id; 87562306a36Sopenharmony_ci } 87662306a36Sopenharmony_ci 87762306a36Sopenharmony_ci /* Connect to link-local address requires an interface */ 87862306a36Sopenharmony_ci if (!sk->sk_bound_dev_if) 87962306a36Sopenharmony_ci return -EINVAL; 88062306a36Sopenharmony_ci } 88162306a36Sopenharmony_ci 88262306a36Sopenharmony_ci sk->sk_v6_daddr = usin->sin6_addr; 88362306a36Sopenharmony_ci np->flow_label = fl6.flowlabel; 88462306a36Sopenharmony_ci 88562306a36Sopenharmony_ci /* 88662306a36Sopenharmony_ci * DCCP over IPv4 88762306a36Sopenharmony_ci */ 88862306a36Sopenharmony_ci if (addr_type == IPV6_ADDR_MAPPED) { 88962306a36Sopenharmony_ci u32 exthdrlen = icsk->icsk_ext_hdr_len; 89062306a36Sopenharmony_ci struct sockaddr_in sin; 89162306a36Sopenharmony_ci 89262306a36Sopenharmony_ci SOCK_DEBUG(sk, "connect: ipv4 mapped\n"); 89362306a36Sopenharmony_ci 89462306a36Sopenharmony_ci if (ipv6_only_sock(sk)) 89562306a36Sopenharmony_ci return -ENETUNREACH; 89662306a36Sopenharmony_ci 89762306a36Sopenharmony_ci sin.sin_family = AF_INET; 89862306a36Sopenharmony_ci sin.sin_port = usin->sin6_port; 89962306a36Sopenharmony_ci sin.sin_addr.s_addr = usin->sin6_addr.s6_addr32[3]; 90062306a36Sopenharmony_ci 90162306a36Sopenharmony_ci icsk->icsk_af_ops = &dccp_ipv6_mapped; 90262306a36Sopenharmony_ci sk->sk_backlog_rcv = dccp_v4_do_rcv; 90362306a36Sopenharmony_ci 90462306a36Sopenharmony_ci err = dccp_v4_connect(sk, (struct sockaddr *)&sin, sizeof(sin)); 90562306a36Sopenharmony_ci if (err) { 90662306a36Sopenharmony_ci icsk->icsk_ext_hdr_len = exthdrlen; 90762306a36Sopenharmony_ci icsk->icsk_af_ops = &dccp_ipv6_af_ops; 90862306a36Sopenharmony_ci sk->sk_backlog_rcv = dccp_v6_do_rcv; 90962306a36Sopenharmony_ci goto failure; 91062306a36Sopenharmony_ci } 91162306a36Sopenharmony_ci np->saddr = sk->sk_v6_rcv_saddr; 91262306a36Sopenharmony_ci return err; 91362306a36Sopenharmony_ci } 91462306a36Sopenharmony_ci 91562306a36Sopenharmony_ci if (!ipv6_addr_any(&sk->sk_v6_rcv_saddr)) 91662306a36Sopenharmony_ci saddr = &sk->sk_v6_rcv_saddr; 91762306a36Sopenharmony_ci 91862306a36Sopenharmony_ci fl6.flowi6_proto = IPPROTO_DCCP; 91962306a36Sopenharmony_ci fl6.daddr = sk->sk_v6_daddr; 92062306a36Sopenharmony_ci fl6.saddr = saddr ? *saddr : np->saddr; 92162306a36Sopenharmony_ci fl6.flowi6_oif = sk->sk_bound_dev_if; 92262306a36Sopenharmony_ci fl6.fl6_dport = usin->sin6_port; 92362306a36Sopenharmony_ci fl6.fl6_sport = inet->inet_sport; 92462306a36Sopenharmony_ci security_sk_classify_flow(sk, flowi6_to_flowi_common(&fl6)); 92562306a36Sopenharmony_ci 92662306a36Sopenharmony_ci opt = rcu_dereference_protected(np->opt, lockdep_sock_is_held(sk)); 92762306a36Sopenharmony_ci final_p = fl6_update_dst(&fl6, opt, &final); 92862306a36Sopenharmony_ci 92962306a36Sopenharmony_ci dst = ip6_dst_lookup_flow(sock_net(sk), sk, &fl6, final_p); 93062306a36Sopenharmony_ci if (IS_ERR(dst)) { 93162306a36Sopenharmony_ci err = PTR_ERR(dst); 93262306a36Sopenharmony_ci goto failure; 93362306a36Sopenharmony_ci } 93462306a36Sopenharmony_ci 93562306a36Sopenharmony_ci if (saddr == NULL) { 93662306a36Sopenharmony_ci saddr = &fl6.saddr; 93762306a36Sopenharmony_ci 93862306a36Sopenharmony_ci err = inet_bhash2_update_saddr(sk, saddr, AF_INET6); 93962306a36Sopenharmony_ci if (err) 94062306a36Sopenharmony_ci goto failure; 94162306a36Sopenharmony_ci } 94262306a36Sopenharmony_ci 94362306a36Sopenharmony_ci /* set the source address */ 94462306a36Sopenharmony_ci np->saddr = *saddr; 94562306a36Sopenharmony_ci inet->inet_rcv_saddr = LOOPBACK4_IPV6; 94662306a36Sopenharmony_ci 94762306a36Sopenharmony_ci ip6_dst_store(sk, dst, NULL, NULL); 94862306a36Sopenharmony_ci 94962306a36Sopenharmony_ci icsk->icsk_ext_hdr_len = 0; 95062306a36Sopenharmony_ci if (opt) 95162306a36Sopenharmony_ci icsk->icsk_ext_hdr_len = opt->opt_flen + opt->opt_nflen; 95262306a36Sopenharmony_ci 95362306a36Sopenharmony_ci inet->inet_dport = usin->sin6_port; 95462306a36Sopenharmony_ci 95562306a36Sopenharmony_ci dccp_set_state(sk, DCCP_REQUESTING); 95662306a36Sopenharmony_ci err = inet6_hash_connect(&dccp_death_row, sk); 95762306a36Sopenharmony_ci if (err) 95862306a36Sopenharmony_ci goto late_failure; 95962306a36Sopenharmony_ci 96062306a36Sopenharmony_ci dp->dccps_iss = secure_dccpv6_sequence_number(np->saddr.s6_addr32, 96162306a36Sopenharmony_ci sk->sk_v6_daddr.s6_addr32, 96262306a36Sopenharmony_ci inet->inet_sport, 96362306a36Sopenharmony_ci inet->inet_dport); 96462306a36Sopenharmony_ci err = dccp_connect(sk); 96562306a36Sopenharmony_ci if (err) 96662306a36Sopenharmony_ci goto late_failure; 96762306a36Sopenharmony_ci 96862306a36Sopenharmony_ci return 0; 96962306a36Sopenharmony_ci 97062306a36Sopenharmony_cilate_failure: 97162306a36Sopenharmony_ci dccp_set_state(sk, DCCP_CLOSED); 97262306a36Sopenharmony_ci inet_bhash2_reset_saddr(sk); 97362306a36Sopenharmony_ci __sk_dst_reset(sk); 97462306a36Sopenharmony_cifailure: 97562306a36Sopenharmony_ci inet->inet_dport = 0; 97662306a36Sopenharmony_ci sk->sk_route_caps = 0; 97762306a36Sopenharmony_ci return err; 97862306a36Sopenharmony_ci} 97962306a36Sopenharmony_ci 98062306a36Sopenharmony_cistatic const struct inet_connection_sock_af_ops dccp_ipv6_af_ops = { 98162306a36Sopenharmony_ci .queue_xmit = inet6_csk_xmit, 98262306a36Sopenharmony_ci .send_check = dccp_v6_send_check, 98362306a36Sopenharmony_ci .rebuild_header = inet6_sk_rebuild_header, 98462306a36Sopenharmony_ci .conn_request = dccp_v6_conn_request, 98562306a36Sopenharmony_ci .syn_recv_sock = dccp_v6_request_recv_sock, 98662306a36Sopenharmony_ci .net_header_len = sizeof(struct ipv6hdr), 98762306a36Sopenharmony_ci .setsockopt = ipv6_setsockopt, 98862306a36Sopenharmony_ci .getsockopt = ipv6_getsockopt, 98962306a36Sopenharmony_ci .addr2sockaddr = inet6_csk_addr2sockaddr, 99062306a36Sopenharmony_ci .sockaddr_len = sizeof(struct sockaddr_in6), 99162306a36Sopenharmony_ci}; 99262306a36Sopenharmony_ci 99362306a36Sopenharmony_ci/* 99462306a36Sopenharmony_ci * DCCP over IPv4 via INET6 API 99562306a36Sopenharmony_ci */ 99662306a36Sopenharmony_cistatic const struct inet_connection_sock_af_ops dccp_ipv6_mapped = { 99762306a36Sopenharmony_ci .queue_xmit = ip_queue_xmit, 99862306a36Sopenharmony_ci .send_check = dccp_v4_send_check, 99962306a36Sopenharmony_ci .rebuild_header = inet_sk_rebuild_header, 100062306a36Sopenharmony_ci .conn_request = dccp_v6_conn_request, 100162306a36Sopenharmony_ci .syn_recv_sock = dccp_v6_request_recv_sock, 100262306a36Sopenharmony_ci .net_header_len = sizeof(struct iphdr), 100362306a36Sopenharmony_ci .setsockopt = ipv6_setsockopt, 100462306a36Sopenharmony_ci .getsockopt = ipv6_getsockopt, 100562306a36Sopenharmony_ci .addr2sockaddr = inet6_csk_addr2sockaddr, 100662306a36Sopenharmony_ci .sockaddr_len = sizeof(struct sockaddr_in6), 100762306a36Sopenharmony_ci}; 100862306a36Sopenharmony_ci 100962306a36Sopenharmony_cistatic void dccp_v6_sk_destruct(struct sock *sk) 101062306a36Sopenharmony_ci{ 101162306a36Sopenharmony_ci dccp_destruct_common(sk); 101262306a36Sopenharmony_ci inet6_sock_destruct(sk); 101362306a36Sopenharmony_ci} 101462306a36Sopenharmony_ci 101562306a36Sopenharmony_ci/* NOTE: A lot of things set to zero explicitly by call to 101662306a36Sopenharmony_ci * sk_alloc() so need not be done here. 101762306a36Sopenharmony_ci */ 101862306a36Sopenharmony_cistatic int dccp_v6_init_sock(struct sock *sk) 101962306a36Sopenharmony_ci{ 102062306a36Sopenharmony_ci static __u8 dccp_v6_ctl_sock_initialized; 102162306a36Sopenharmony_ci int err = dccp_init_sock(sk, dccp_v6_ctl_sock_initialized); 102262306a36Sopenharmony_ci 102362306a36Sopenharmony_ci if (err == 0) { 102462306a36Sopenharmony_ci if (unlikely(!dccp_v6_ctl_sock_initialized)) 102562306a36Sopenharmony_ci dccp_v6_ctl_sock_initialized = 1; 102662306a36Sopenharmony_ci inet_csk(sk)->icsk_af_ops = &dccp_ipv6_af_ops; 102762306a36Sopenharmony_ci sk->sk_destruct = dccp_v6_sk_destruct; 102862306a36Sopenharmony_ci } 102962306a36Sopenharmony_ci 103062306a36Sopenharmony_ci return err; 103162306a36Sopenharmony_ci} 103262306a36Sopenharmony_ci 103362306a36Sopenharmony_cistatic struct timewait_sock_ops dccp6_timewait_sock_ops = { 103462306a36Sopenharmony_ci .twsk_obj_size = sizeof(struct dccp6_timewait_sock), 103562306a36Sopenharmony_ci}; 103662306a36Sopenharmony_ci 103762306a36Sopenharmony_cistatic struct proto dccp_v6_prot = { 103862306a36Sopenharmony_ci .name = "DCCPv6", 103962306a36Sopenharmony_ci .owner = THIS_MODULE, 104062306a36Sopenharmony_ci .close = dccp_close, 104162306a36Sopenharmony_ci .connect = dccp_v6_connect, 104262306a36Sopenharmony_ci .disconnect = dccp_disconnect, 104362306a36Sopenharmony_ci .ioctl = dccp_ioctl, 104462306a36Sopenharmony_ci .init = dccp_v6_init_sock, 104562306a36Sopenharmony_ci .setsockopt = dccp_setsockopt, 104662306a36Sopenharmony_ci .getsockopt = dccp_getsockopt, 104762306a36Sopenharmony_ci .sendmsg = dccp_sendmsg, 104862306a36Sopenharmony_ci .recvmsg = dccp_recvmsg, 104962306a36Sopenharmony_ci .backlog_rcv = dccp_v6_do_rcv, 105062306a36Sopenharmony_ci .hash = inet6_hash, 105162306a36Sopenharmony_ci .unhash = inet_unhash, 105262306a36Sopenharmony_ci .accept = inet_csk_accept, 105362306a36Sopenharmony_ci .get_port = inet_csk_get_port, 105462306a36Sopenharmony_ci .shutdown = dccp_shutdown, 105562306a36Sopenharmony_ci .destroy = dccp_destroy_sock, 105662306a36Sopenharmony_ci .orphan_count = &dccp_orphan_count, 105762306a36Sopenharmony_ci .max_header = MAX_DCCP_HEADER, 105862306a36Sopenharmony_ci .obj_size = sizeof(struct dccp6_sock), 105962306a36Sopenharmony_ci .ipv6_pinfo_offset = offsetof(struct dccp6_sock, inet6), 106062306a36Sopenharmony_ci .slab_flags = SLAB_TYPESAFE_BY_RCU, 106162306a36Sopenharmony_ci .rsk_prot = &dccp6_request_sock_ops, 106262306a36Sopenharmony_ci .twsk_prot = &dccp6_timewait_sock_ops, 106362306a36Sopenharmony_ci .h.hashinfo = &dccp_hashinfo, 106462306a36Sopenharmony_ci}; 106562306a36Sopenharmony_ci 106662306a36Sopenharmony_cistatic const struct inet6_protocol dccp_v6_protocol = { 106762306a36Sopenharmony_ci .handler = dccp_v6_rcv, 106862306a36Sopenharmony_ci .err_handler = dccp_v6_err, 106962306a36Sopenharmony_ci .flags = INET6_PROTO_NOPOLICY | INET6_PROTO_FINAL, 107062306a36Sopenharmony_ci}; 107162306a36Sopenharmony_ci 107262306a36Sopenharmony_cistatic const struct proto_ops inet6_dccp_ops = { 107362306a36Sopenharmony_ci .family = PF_INET6, 107462306a36Sopenharmony_ci .owner = THIS_MODULE, 107562306a36Sopenharmony_ci .release = inet6_release, 107662306a36Sopenharmony_ci .bind = inet6_bind, 107762306a36Sopenharmony_ci .connect = inet_stream_connect, 107862306a36Sopenharmony_ci .socketpair = sock_no_socketpair, 107962306a36Sopenharmony_ci .accept = inet_accept, 108062306a36Sopenharmony_ci .getname = inet6_getname, 108162306a36Sopenharmony_ci .poll = dccp_poll, 108262306a36Sopenharmony_ci .ioctl = inet6_ioctl, 108362306a36Sopenharmony_ci .gettstamp = sock_gettstamp, 108462306a36Sopenharmony_ci .listen = inet_dccp_listen, 108562306a36Sopenharmony_ci .shutdown = inet_shutdown, 108662306a36Sopenharmony_ci .setsockopt = sock_common_setsockopt, 108762306a36Sopenharmony_ci .getsockopt = sock_common_getsockopt, 108862306a36Sopenharmony_ci .sendmsg = inet_sendmsg, 108962306a36Sopenharmony_ci .recvmsg = sock_common_recvmsg, 109062306a36Sopenharmony_ci .mmap = sock_no_mmap, 109162306a36Sopenharmony_ci#ifdef CONFIG_COMPAT 109262306a36Sopenharmony_ci .compat_ioctl = inet6_compat_ioctl, 109362306a36Sopenharmony_ci#endif 109462306a36Sopenharmony_ci}; 109562306a36Sopenharmony_ci 109662306a36Sopenharmony_cistatic struct inet_protosw dccp_v6_protosw = { 109762306a36Sopenharmony_ci .type = SOCK_DCCP, 109862306a36Sopenharmony_ci .protocol = IPPROTO_DCCP, 109962306a36Sopenharmony_ci .prot = &dccp_v6_prot, 110062306a36Sopenharmony_ci .ops = &inet6_dccp_ops, 110162306a36Sopenharmony_ci .flags = INET_PROTOSW_ICSK, 110262306a36Sopenharmony_ci}; 110362306a36Sopenharmony_ci 110462306a36Sopenharmony_cistatic int __net_init dccp_v6_init_net(struct net *net) 110562306a36Sopenharmony_ci{ 110662306a36Sopenharmony_ci struct dccp_v6_pernet *pn = net_generic(net, dccp_v6_pernet_id); 110762306a36Sopenharmony_ci 110862306a36Sopenharmony_ci if (dccp_hashinfo.bhash == NULL) 110962306a36Sopenharmony_ci return -ESOCKTNOSUPPORT; 111062306a36Sopenharmony_ci 111162306a36Sopenharmony_ci return inet_ctl_sock_create(&pn->v6_ctl_sk, PF_INET6, 111262306a36Sopenharmony_ci SOCK_DCCP, IPPROTO_DCCP, net); 111362306a36Sopenharmony_ci} 111462306a36Sopenharmony_ci 111562306a36Sopenharmony_cistatic void __net_exit dccp_v6_exit_net(struct net *net) 111662306a36Sopenharmony_ci{ 111762306a36Sopenharmony_ci struct dccp_v6_pernet *pn = net_generic(net, dccp_v6_pernet_id); 111862306a36Sopenharmony_ci 111962306a36Sopenharmony_ci inet_ctl_sock_destroy(pn->v6_ctl_sk); 112062306a36Sopenharmony_ci} 112162306a36Sopenharmony_ci 112262306a36Sopenharmony_cistatic void __net_exit dccp_v6_exit_batch(struct list_head *net_exit_list) 112362306a36Sopenharmony_ci{ 112462306a36Sopenharmony_ci inet_twsk_purge(&dccp_hashinfo, AF_INET6); 112562306a36Sopenharmony_ci} 112662306a36Sopenharmony_ci 112762306a36Sopenharmony_cistatic struct pernet_operations dccp_v6_ops = { 112862306a36Sopenharmony_ci .init = dccp_v6_init_net, 112962306a36Sopenharmony_ci .exit = dccp_v6_exit_net, 113062306a36Sopenharmony_ci .exit_batch = dccp_v6_exit_batch, 113162306a36Sopenharmony_ci .id = &dccp_v6_pernet_id, 113262306a36Sopenharmony_ci .size = sizeof(struct dccp_v6_pernet), 113362306a36Sopenharmony_ci}; 113462306a36Sopenharmony_ci 113562306a36Sopenharmony_cistatic int __init dccp_v6_init(void) 113662306a36Sopenharmony_ci{ 113762306a36Sopenharmony_ci int err = proto_register(&dccp_v6_prot, 1); 113862306a36Sopenharmony_ci 113962306a36Sopenharmony_ci if (err) 114062306a36Sopenharmony_ci goto out; 114162306a36Sopenharmony_ci 114262306a36Sopenharmony_ci inet6_register_protosw(&dccp_v6_protosw); 114362306a36Sopenharmony_ci 114462306a36Sopenharmony_ci err = register_pernet_subsys(&dccp_v6_ops); 114562306a36Sopenharmony_ci if (err) 114662306a36Sopenharmony_ci goto out_destroy_ctl_sock; 114762306a36Sopenharmony_ci 114862306a36Sopenharmony_ci err = inet6_add_protocol(&dccp_v6_protocol, IPPROTO_DCCP); 114962306a36Sopenharmony_ci if (err) 115062306a36Sopenharmony_ci goto out_unregister_proto; 115162306a36Sopenharmony_ci 115262306a36Sopenharmony_ciout: 115362306a36Sopenharmony_ci return err; 115462306a36Sopenharmony_ciout_unregister_proto: 115562306a36Sopenharmony_ci unregister_pernet_subsys(&dccp_v6_ops); 115662306a36Sopenharmony_ciout_destroy_ctl_sock: 115762306a36Sopenharmony_ci inet6_unregister_protosw(&dccp_v6_protosw); 115862306a36Sopenharmony_ci proto_unregister(&dccp_v6_prot); 115962306a36Sopenharmony_ci goto out; 116062306a36Sopenharmony_ci} 116162306a36Sopenharmony_ci 116262306a36Sopenharmony_cistatic void __exit dccp_v6_exit(void) 116362306a36Sopenharmony_ci{ 116462306a36Sopenharmony_ci inet6_del_protocol(&dccp_v6_protocol, IPPROTO_DCCP); 116562306a36Sopenharmony_ci unregister_pernet_subsys(&dccp_v6_ops); 116662306a36Sopenharmony_ci inet6_unregister_protosw(&dccp_v6_protosw); 116762306a36Sopenharmony_ci proto_unregister(&dccp_v6_prot); 116862306a36Sopenharmony_ci} 116962306a36Sopenharmony_ci 117062306a36Sopenharmony_cimodule_init(dccp_v6_init); 117162306a36Sopenharmony_cimodule_exit(dccp_v6_exit); 117262306a36Sopenharmony_ci 117362306a36Sopenharmony_ci/* 117462306a36Sopenharmony_ci * __stringify doesn't likes enums, so use SOCK_DCCP (6) and IPPROTO_DCCP (33) 117562306a36Sopenharmony_ci * values directly, Also cover the case where the protocol is not specified, 117662306a36Sopenharmony_ci * i.e. net-pf-PF_INET6-proto-0-type-SOCK_DCCP 117762306a36Sopenharmony_ci */ 117862306a36Sopenharmony_ciMODULE_ALIAS_NET_PF_PROTO_TYPE(PF_INET6, 33, 6); 117962306a36Sopenharmony_ciMODULE_ALIAS_NET_PF_PROTO_TYPE(PF_INET6, 0, 6); 118062306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 118162306a36Sopenharmony_ciMODULE_AUTHOR("Arnaldo Carvalho de Melo <acme@mandriva.com>"); 118262306a36Sopenharmony_ciMODULE_DESCRIPTION("DCCPv6 - Datagram Congestion Controlled Protocol"); 1183