1 | // SPDX-License-Identifier: GPL-2.0-or-later |
2 | /* |
3 | * net/dccp/minisocks.c |
4 | * |
5 | * An implementation of the DCCP protocol |
6 | * Arnaldo Carvalho de Melo <acme@conectiva.com.br> |
7 | */ |
8 | |
9 | #include <linux/dccp.h> |
10 | #include <linux/gfp.h> |
11 | #include <linux/kernel.h> |
12 | #include <linux/skbuff.h> |
13 | #include <linux/timer.h> |
14 | |
15 | #include <net/sock.h> |
16 | #include <net/xfrm.h> |
17 | #include <net/inet_timewait_sock.h> |
18 | |
19 | #include "ackvec.h" |
20 | #include "ccid.h" |
21 | #include "dccp.h" |
22 | #include "feat.h" |
23 | |
24 | struct inet_timewait_death_row dccp_death_row = { |
25 | .tw_refcount = REFCOUNT_INIT(1), |
26 | .sysctl_max_tw_buckets = NR_FILE * 2, |
27 | .hashinfo = &dccp_hashinfo, |
28 | }; |
29 | |
30 | EXPORT_SYMBOL_GPL(dccp_death_row); |
31 | |
32 | void dccp_time_wait(struct sock *sk, int state, int timeo) |
33 | { |
34 | struct inet_timewait_sock *tw; |
35 | |
36 | tw = inet_twsk_alloc(sk, dr: &dccp_death_row, state); |
37 | |
38 | if (tw != NULL) { |
39 | const struct inet_connection_sock *icsk = inet_csk(sk); |
40 | const int rto = (icsk->icsk_rto << 2) - (icsk->icsk_rto >> 1); |
41 | #if IS_ENABLED(CONFIG_IPV6) |
42 | if (tw->tw_family == PF_INET6) { |
43 | tw->tw_v6_daddr = sk->sk_v6_daddr; |
44 | tw->tw_v6_rcv_saddr = sk->sk_v6_rcv_saddr; |
45 | tw->tw_ipv6only = sk->sk_ipv6only; |
46 | } |
47 | #endif |
48 | |
49 | /* Get the TIME_WAIT timeout firing. */ |
50 | if (timeo < rto) |
51 | timeo = rto; |
52 | |
53 | if (state == DCCP_TIME_WAIT) |
54 | timeo = DCCP_TIMEWAIT_LEN; |
55 | |
56 | /* tw_timer is pinned, so we need to make sure BH are disabled |
57 | * in following section, otherwise timer handler could run before |
58 | * we complete the initialization. |
59 | */ |
60 | local_bh_disable(); |
61 | inet_twsk_schedule(tw, timeo); |
62 | /* Linkage updates. |
63 | * Note that access to tw after this point is illegal. |
64 | */ |
65 | inet_twsk_hashdance(tw, sk, hashinfo: &dccp_hashinfo); |
66 | local_bh_enable(); |
67 | } else { |
68 | /* Sorry, if we're out of memory, just CLOSE this |
69 | * socket up. We've got bigger problems than |
70 | * non-graceful socket closings. |
71 | */ |
72 | DCCP_WARN("time wait bucket table overflow\n" ); |
73 | } |
74 | |
75 | dccp_done(sk); |
76 | } |
77 | |
78 | struct sock *dccp_create_openreq_child(const struct sock *sk, |
79 | const struct request_sock *req, |
80 | const struct sk_buff *skb) |
81 | { |
82 | /* |
83 | * Step 3: Process LISTEN state |
84 | * |
85 | * (* Generate a new socket and switch to that socket *) |
86 | * Set S := new socket for this port pair |
87 | */ |
88 | struct sock *newsk = inet_csk_clone_lock(sk, req, GFP_ATOMIC); |
89 | |
90 | if (newsk != NULL) { |
91 | struct dccp_request_sock *dreq = dccp_rsk(req); |
92 | struct inet_connection_sock *newicsk = inet_csk(sk: newsk); |
93 | struct dccp_sock *newdp = dccp_sk(newsk); |
94 | |
95 | newdp->dccps_role = DCCP_ROLE_SERVER; |
96 | newdp->dccps_hc_rx_ackvec = NULL; |
97 | newdp->dccps_service_list = NULL; |
98 | newdp->dccps_hc_rx_ccid = NULL; |
99 | newdp->dccps_hc_tx_ccid = NULL; |
100 | newdp->dccps_service = dreq->dreq_service; |
101 | newdp->dccps_timestamp_echo = dreq->dreq_timestamp_echo; |
102 | newdp->dccps_timestamp_time = dreq->dreq_timestamp_time; |
103 | newicsk->icsk_rto = DCCP_TIMEOUT_INIT; |
104 | |
105 | INIT_LIST_HEAD(list: &newdp->dccps_featneg); |
106 | /* |
107 | * Step 3: Process LISTEN state |
108 | * |
109 | * Choose S.ISS (initial seqno) or set from Init Cookies |
110 | * Initialize S.GAR := S.ISS |
111 | * Set S.ISR, S.GSR from packet (or Init Cookies) |
112 | * |
113 | * Setting AWL/AWH and SWL/SWH happens as part of the feature |
114 | * activation below, as these windows all depend on the local |
115 | * and remote Sequence Window feature values (7.5.2). |
116 | */ |
117 | newdp->dccps_iss = dreq->dreq_iss; |
118 | newdp->dccps_gss = dreq->dreq_gss; |
119 | newdp->dccps_gar = newdp->dccps_iss; |
120 | newdp->dccps_isr = dreq->dreq_isr; |
121 | newdp->dccps_gsr = dreq->dreq_gsr; |
122 | |
123 | /* |
124 | * Activate features: initialise CCIDs, sequence windows etc. |
125 | */ |
126 | if (dccp_feat_activate_values(sk: newsk, fn: &dreq->dreq_featneg)) { |
127 | sk_free_unlock_clone(sk: newsk); |
128 | return NULL; |
129 | } |
130 | dccp_init_xmit_timers(sk: newsk); |
131 | |
132 | __DCCP_INC_STATS(DCCP_MIB_PASSIVEOPENS); |
133 | } |
134 | return newsk; |
135 | } |
136 | |
137 | EXPORT_SYMBOL_GPL(dccp_create_openreq_child); |
138 | |
139 | /* |
140 | * Process an incoming packet for RESPOND sockets represented |
141 | * as an request_sock. |
142 | */ |
143 | struct sock *dccp_check_req(struct sock *sk, struct sk_buff *skb, |
144 | struct request_sock *req) |
145 | { |
146 | struct sock *child = NULL; |
147 | struct dccp_request_sock *dreq = dccp_rsk(req); |
148 | bool own_req; |
149 | |
150 | /* TCP/DCCP listeners became lockless. |
151 | * DCCP stores complex state in its request_sock, so we need |
152 | * a protection for them, now this code runs without being protected |
153 | * by the parent (listener) lock. |
154 | */ |
155 | spin_lock_bh(lock: &dreq->dreq_lock); |
156 | |
157 | /* Check for retransmitted REQUEST */ |
158 | if (dccp_hdr(skb)->dccph_type == DCCP_PKT_REQUEST) { |
159 | |
160 | if (after48(DCCP_SKB_CB(skb)->dccpd_seq, dreq->dreq_gsr)) { |
161 | dccp_pr_debug("Retransmitted REQUEST\n" ); |
162 | dreq->dreq_gsr = DCCP_SKB_CB(skb)->dccpd_seq; |
163 | /* |
164 | * Send another RESPONSE packet |
165 | * To protect against Request floods, increment retrans |
166 | * counter (backoff, monitored by dccp_response_timer). |
167 | */ |
168 | inet_rtx_syn_ack(parent: sk, req); |
169 | } |
170 | /* Network Duplicate, discard packet */ |
171 | goto out; |
172 | } |
173 | |
174 | DCCP_SKB_CB(skb)->dccpd_reset_code = DCCP_RESET_CODE_PACKET_ERROR; |
175 | |
176 | if (dccp_hdr(skb)->dccph_type != DCCP_PKT_ACK && |
177 | dccp_hdr(skb)->dccph_type != DCCP_PKT_DATAACK) |
178 | goto drop; |
179 | |
180 | /* Invalid ACK */ |
181 | if (!between48(DCCP_SKB_CB(skb)->dccpd_ack_seq, |
182 | seq2: dreq->dreq_iss, seq3: dreq->dreq_gss)) { |
183 | dccp_pr_debug("Invalid ACK number: ack_seq=%llu, " |
184 | "dreq_iss=%llu, dreq_gss=%llu\n" , |
185 | (unsigned long long) |
186 | DCCP_SKB_CB(skb)->dccpd_ack_seq, |
187 | (unsigned long long) dreq->dreq_iss, |
188 | (unsigned long long) dreq->dreq_gss); |
189 | goto drop; |
190 | } |
191 | |
192 | if (dccp_parse_options(sk, dreq, skb)) |
193 | goto drop; |
194 | |
195 | child = inet_csk(sk)->icsk_af_ops->syn_recv_sock(sk, skb, req, NULL, |
196 | req, &own_req); |
197 | if (child) { |
198 | child = inet_csk_complete_hashdance(sk, child, req, own_req); |
199 | goto out; |
200 | } |
201 | |
202 | DCCP_SKB_CB(skb)->dccpd_reset_code = DCCP_RESET_CODE_TOO_BUSY; |
203 | drop: |
204 | if (dccp_hdr(skb)->dccph_type != DCCP_PKT_RESET) |
205 | req->rsk_ops->send_reset(sk, skb); |
206 | |
207 | inet_csk_reqsk_queue_drop(sk, req); |
208 | out: |
209 | spin_unlock_bh(lock: &dreq->dreq_lock); |
210 | return child; |
211 | } |
212 | |
213 | EXPORT_SYMBOL_GPL(dccp_check_req); |
214 | |
215 | /* |
216 | * Queue segment on the new socket if the new socket is active, |
217 | * otherwise we just shortcircuit this and continue with |
218 | * the new socket. |
219 | */ |
220 | int dccp_child_process(struct sock *parent, struct sock *child, |
221 | struct sk_buff *skb) |
222 | __releases(child) |
223 | { |
224 | int ret = 0; |
225 | const int state = child->sk_state; |
226 | |
227 | if (!sock_owned_by_user(sk: child)) { |
228 | ret = dccp_rcv_state_process(sk: child, skb, dh: dccp_hdr(skb), |
229 | len: skb->len); |
230 | |
231 | /* Wakeup parent, send SIGIO */ |
232 | if (state == DCCP_RESPOND && child->sk_state != state) |
233 | parent->sk_data_ready(parent); |
234 | } else { |
235 | /* Alas, it is possible again, because we do lookup |
236 | * in main socket hash table and lock on listening |
237 | * socket does not protect us more. |
238 | */ |
239 | __sk_add_backlog(sk: child, skb); |
240 | } |
241 | |
242 | bh_unlock_sock(child); |
243 | sock_put(sk: child); |
244 | return ret; |
245 | } |
246 | |
247 | EXPORT_SYMBOL_GPL(dccp_child_process); |
248 | |
249 | void dccp_reqsk_send_ack(const struct sock *sk, struct sk_buff *skb, |
250 | struct request_sock *rsk) |
251 | { |
252 | DCCP_BUG("DCCP-ACK packets are never sent in LISTEN/RESPOND state" ); |
253 | } |
254 | |
255 | EXPORT_SYMBOL_GPL(dccp_reqsk_send_ack); |
256 | |
257 | int dccp_reqsk_init(struct request_sock *req, |
258 | struct dccp_sock const *dp, struct sk_buff const *skb) |
259 | { |
260 | struct dccp_request_sock *dreq = dccp_rsk(req); |
261 | |
262 | spin_lock_init(&dreq->dreq_lock); |
263 | inet_rsk(sk: req)->ir_rmt_port = dccp_hdr(skb)->dccph_sport; |
264 | inet_rsk(sk: req)->ir_num = ntohs(dccp_hdr(skb)->dccph_dport); |
265 | inet_rsk(sk: req)->acked = 0; |
266 | dreq->dreq_timestamp_echo = 0; |
267 | |
268 | /* inherit feature negotiation options from listening socket */ |
269 | return dccp_feat_clone_list(&dp->dccps_featneg, &dreq->dreq_featneg); |
270 | } |
271 | |
272 | EXPORT_SYMBOL_GPL(dccp_reqsk_init); |
273 | |